使用最大流和費用流解決二分圖的多重匹配
之前編輯的忘存了好氣啊。。
本來打算學完二分圖的亂七八糟的匹配之后再去接觸網絡流的,提前撞到了
之前我們說的二分圖最大匹配和二分圖最大權匹配有一個特點,那就是沒個點只能與一條邊相匹配
如果規定一個點要與L條邊相匹配,這樣的問題就是二分圖的多重匹配問題
然后根據邊是否帶權重,又可以分為二分圖最大多重匹配和二分圖最大權多重匹配(二分圖多重最佳完美匹配)
首先給出二分圖多重最大匹配的做法:
在原圖上建立源點S和匯點T,S向每個X方點連一條容量為該X方點L值的邊,每個Y方點向T連一條容量為該Y方點L值的邊
原來二分圖中各邊在新的網絡中仍存在,容量為1(若該邊可以使用多次則容量大於1),求該網絡的最大流,就是該二分圖多重最大匹配的值
然后給出二分圖多重最優匹配(二分圖多重最大權匹配)的做法:
在原圖上建立源點S和匯點T,S向每個X方點連一條容量為該X方點L值、費用為0的邊,每個Y方點向T連一條容量為該Y方點L值、費用為0的邊
原來二分圖中各邊在新的網絡中仍存在,容量為1(若該邊可以使用多次則容量大於1),費用為該邊的權值。求該網絡的最大費用最大流,就是該二分圖多重最優匹配的值
這道題里面,一共有X方點這么多的電影,每個電影需要拍攝多少天就是對應的X方點L值,然后每一天是一個Y方點,由於每一天只能拍攝一部電影,所有Y方點的L值均為1
下面介紹一下實現:
int n,sum,cnt,ans; int g[maxn],cur[maxn]; int str[25][10]; struct Edge{int u,v,next,cap,flow;}e[maxm];
這里面的cur數組是g數組的臨時數組
str用來保存每一個電影可以在哪一天拍攝
Edge是網絡流圖里面的邊
void addedge(int u,int v,int c) { e[++cnt].u=u;e[cnt].v=v;e[cnt].cap=c; e[cnt].flow=0;e[cnt].next=g[u];g[u]=cnt; e[++cnt].u=v;e[cnt].v=u;e[cnt].cap=0; e[cnt].flow=0;e[cnt].next=g[v];g[v]=cnt; }
建圖的時候,注意怎么賦值的
接下來根據題意建圖:
for(int i=1;i<=n;i++) { for(int j=1;j<=7;j++) scanf("%d",&str[i][j]); scanf("%d%d",&d,&w); sum+=d; addedge(0,i,d); //容量為需要多少天 for(int j=1;j<=7;j++) for(int k=0;k<w;k++) if(str[i][j]) addedge(i,20+k*7+j,1); } for(int i=21;i<=370;i++) addedge(i,371,1); ans=maxflow(0,371);
0為源點,371為匯點
sum最后進行一個統計,和源點出發的最大流量進行比較,如果相等,說明電影排的開
然后是求最大流的一個板子
int maxflow(int st,int ed) { int flowsum=0; while(bfs(st,ed)) { memcpy(cur,g,sizeof(g)); flowsum+=dfs(st,ed,INF); //cout<<"#"<<flowsum<<" "; } return flowsum; }
具體的DFS和BFS這里不作為重點,以后再說
下面給出完整的實現:
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int INF=1000000000; 6 const int maxn=1005; 7 const int maxm=20005; 8 int n,sum,cnt,ans; 9 int g[maxn],cur[maxn]; 10 int str[25][10]; 11 struct Edge{int u,v,next,cap,flow;}e[maxm]; 12 void addedge(int u,int v,int c) 13 { 14 e[++cnt].u=u;e[cnt].v=v;e[cnt].cap=c; 15 e[cnt].flow=0;e[cnt].next=g[u];g[u]=cnt; 16 17 e[++cnt].u=v;e[cnt].v=u;e[cnt].cap=0; 18 e[cnt].flow=0;e[cnt].next=g[v];g[v]=cnt; 19 } 20 int q[maxn],vis[maxn],d[maxn]; 21 bool bfs(int st,int ed) 22 { 23 memset(q,0,sizeof(q)); 24 memset(vis,0,sizeof(vis)); 25 memset(d,-1,sizeof(d)); 26 vis[st]=1;d[st]=0; 27 int h=0,t=1; 28 q[t]=st; 29 while(h!=t) 30 { 31 h=h%maxn+1; 32 int u=q[h]; 33 for(int tmp=g[u];tmp;tmp=e[tmp].next) 34 { 35 if(!vis[e[tmp].v]&&e[tmp].cap>e[tmp].flow) 36 { 37 vis[e[tmp].v]=1; 38 d[e[tmp].v]=d[u]+1; 39 if(e[tmp].v==ed) return true; 40 t=t%maxn+1; 41 q[t]=e[tmp].v; 42 } 43 } 44 } 45 return false; 46 } 47 int getpair(int x) 48 { 49 if(x%2==0) 50 return x-1; 51 else return x+1; 52 } 53 int dfs(int x,int ed,int a) 54 { 55 if(x==ed||a==0) return a; 56 int flow=0,f; 57 for(int tmp=cur[x];tmp;tmp=e[tmp].next) 58 { 59 if(d[e[tmp].v]==d[x]+1&&(f=dfs(e[tmp].v,ed,min(a,e[tmp].cap-e[tmp].flow)))>0) 60 { 61 e[tmp].flow+=f; 62 e[getpair(tmp)].flow-=f; 63 a-=f; 64 flow+=f; 65 if(a==0) break; 66 } 67 } 68 return flow; 69 } 70 int maxflow(int st,int ed) 71 { 72 int flowsum=0; 73 while(bfs(st,ed)) 74 { 75 memcpy(cur,g,sizeof(g)); 76 flowsum+=dfs(st,ed,INF); 77 //cout<<"#"<<flowsum<<" "; 78 } 79 return flowsum; 80 81 } 82 void init() 83 { 84 sum=cnt=0; 85 memset(g,0,sizeof(g)); 86 } 87 int main() 88 { 89 int T,d,w; 90 scanf("%d",&T); 91 while(T--) 92 { 93 init(); 94 scanf("%d",&n); 95 for(int i=1;i<=n;i++) 96 { 97 for(int j=1;j<=7;j++) 98 scanf("%d",&str[i][j]); 99 scanf("%d%d",&d,&w); 100 sum+=d; 101 addedge(0,i,d); //容量為需要多少天 102 for(int j=1;j<=7;j++) 103 for(int k=0;k<w;k++) 104 if(str[i][j]) addedge(i,20+k*7+j,1); 105 } 106 for(int i=21;i<=370;i++) addedge(i,371,1); 107 ans=maxflow(0,371); 108 if(ans==sum) printf("Yes\n"); 109 else printf("No\n"); 110 } 111 return 0; 112 }
據說這是典型的最大流題目,然而為了強行安利一波二分圖的多重匹配,就不說成那個了