一般图带权多重匹配(欧拉图+最小费用流)
problem
給定 nnn 個數 {ai}\{a_i\}{ai?},其中 kkk 個 aia_iai? 是奇數,再給一個 n×nn\times nn×n 的矩陣 {ci,j}\{c_{i,j}\}{ci,j?},無論是 aaa 還是 ccc,都保證是非負整數。
現在可以做一類操作:將 ai?1,aj?1a_i-1,a_j-1ai??1,aj??1,花費 ci,jc_{i,j}ci,j? 代價,i,ji,ji,j 可以相同。
要求把所有的 {ai}\{a_i\}{ai?} 全都變為 000。
求最小花費,無解輸出 ?1-1?1。
n≤50,k≤8n\le 50,k\le 8n≤50,k≤8,保證 ci,j=cj,ic_{i,j}=c_{j,i}ci,j?=cj,i?。
solution
顯然,每次操作都會使得 (∑ai)?2(\sum a_i)-2(∑ai?)?2。所以如果 ∑ai\sum a_i∑ai? 是奇數,即 aia_iai? 為奇數的 iii 有奇數個,則無解。
接下來再來解決有解的問題。
先考慮 k=0k=0k=0 的弱化版。
那就存在歐拉回路,要求每個點的入度和出度相同,經典網絡流模型轉化。
將每個點拆成兩個點,各放左右部,與源/匯點的連邊流量設置為 ai2\frac{a_i}22ai??。
花費針對“關系”而言,左右部點之間連邊流量無窮帶花費。
直接跑最小費用流即可。
轉化思考:
如果操作 i,ji,ji,j,就在圖上連一條 (i,j)(i,j)(i,j) 的邊。那么最后這張圖可能有重邊和若干個環。
發現這是一張歐拉圖,存在歐拉回路。我們能找到一種定向方式使得每個點的入度和出度相同。
推出存在一種最優方案使得每個點的入度和出度相同。
將每個點拆成入度點和出度點,轉化成匹配問題。
現在有幾個特殊點是奇數,歐拉回路不存在,變成存在歐拉路徑。
我們先通過若干次操作將這些奇數全都消成偶數,就又轉化成了歐拉回路,就可以套用上面的方法。
轉化思考:
我們通過對圖上進行加邊,使得這張圖最后仍是存在歐拉回路的圖。
由此推出存在一種最優方案使得奇數點的入度和出度只相差 111。
由于 kkk 非常小,我們大可直接狀壓枚舉哪一半的奇數點是入度多 111,剩下的就肯定是出度多 111。
還是轉化成了入度和出度二分圖的匹配問題,仍然跑個最小費用流。
最后求個 min?\minmin 就完了。
code
#include <bits/stdc++.h> using namespace std; #define maxn 200 #define inf 0x7f7f7f7f struct node { int to, nxt, flow, cost; }E[maxn * maxn]; int head[maxn], dis[maxn], lst[maxn], a[maxn], b[maxn], id[maxn]; int c[maxn][maxn]; bool vis[maxn]; int n, cnt, m, s, t; queue < int > q;void addedge( int u, int v, int flow, int cost ) {E[++ cnt] = { v, head[u], flow, cost }, head[u] = cnt;E[++ cnt] = { u, head[v], 0, - cost }, head[v] = cnt; }bool SPFA() {memset( lst, -1, sizeof( lst ) );memset( dis, 0x7f, sizeof( dis ) );dis[s] = 0, q.push( s );while( ! q.empty() ) {int u = q.front(); q.pop(); vis[u] = 0;for( int i = head[u];~ i;i = E[i].nxt ) {int v = E[i].to;if( dis[v] > dis[u] + E[i].cost and E[i].flow ) {dis[v] = dis[u] + E[i].cost; lst[v] = i;if( ! vis[v] ) vis[v] = 1, q.push( v );}}}return ~ lst[t]; }int solve() {memset( head, -1, sizeof( head ) ), cnt = -1;for( int i = 1;i <= n;i ++ )for( int j = 1;j <= n;j ++ )addedge( i, j + n, inf, c[i][j] );for( int i = 1;i <= n;i ++ ) {addedge( s, i, a[i] + b[i] >> 1, 0 );addedge( i + n, t, a[i] - b[i] >> 1, 0 );}int ans = 0;while( SPFA() ) {int flow = inf;for( int i = lst[t];~ i;i = lst[E[i ^ 1].to] )flow = min( flow, E[i].flow );ans += flow * dis[t];for( int i = lst[t];~ i;i = lst[E[i ^ 1].to] ) {E[i ^ 1].flow += flow;E[i].flow -= flow;}}return ans; }int main() {scanf( "%d", &n ); s = 0, t = n << 1 | 1;for( int i = 1;i <= n;i ++ ) {scanf( "%d", &a[i] );if( a[i] & 1 ) id[m ++] = i;}for( int i = 1;i <= n;i ++ )for( int j = 1;j <= n;j ++ )scanf( "%d", &c[i][j] );if( m & 1 ) return ! puts("-1");int ans = inf;for( int i = 0;i < (1 << m);i ++ ) {for( int j = 0;j < m;j ++ )if( i >> j & 1 ) b[id[j]] = 1;else b[id[j]] = -1;if( __builtin_popcount( i ) ^ (m >> 1) ) continue;ans = min( ans, solve() );}printf( "%d\n", ans );return 0; }總結
以上是生活随笔為你收集整理的一般图带权多重匹配(欧拉图+最小费用流)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 研报将比亚迪“仰望&rdqu
- 下一篇: [ZJOI2007] 棋盘制作(单调栈