1. 程式人生 > >POJ 3281 /// 最大流

POJ 3281 /// 最大流

pla color 二分圖匹配 不能 最大流 一點 truct names c++

題目大意:

n 頭牛 f 種食物 d 種飲料

每頭牛有各自喜歡的食物和飲料

求最多有多少頭牛能分配到自己喜歡的食物和飲料

因為同時有食物和飲料 所以不能用二分圖匹配

用最大流解決二分圖匹配的辦法

增加一個源點連向所有食物 每頭牛與各自喜歡的食物連邊

增加一個匯點連向所有的飲料 每頭牛與各自喜歡的飲料連邊

以上邊容量都為1

單純這樣連的話 一頭牛可能分配到多種食物和飲料

把一頭牛拆成兩個點 一點與食物連邊 另一點與飲料連邊

再在兩個點之間連一條容量為1的邊 這樣就能保證只有一個流量流過

即只有一種食物被選 飲料反過來同理

此時從源點到匯點跑個最大流就能得到答案

技術分享圖片
#include <bits/stdc++.h>
#define
INF 0x3f3f3f3f using namespace std; const int N=105; bool F[N][N], D[N][N]; int n, f, d; struct EDGE { int v,c,r; }; vector <EDGE> E[505]; bool vis[505]; void addE(int u,int v,int c) { E[u].push_back((EDGE){v,c,E[v].size()}); E[v].push_back((EDGE){u,0,E[u].size()-1}); } /**最大流*/ int dfs(int
s,int t,int f) { if(s==t) return f; vis[s]=1; for(int i=0;i<E[s].size();i++) { EDGE& e=E[s][i]; // EDGE& 之後可直接修改到這個地址的數值 if(!vis[e.v] && e.c>0) { // 沒走過且可流過 int d=dfs(e.v,t,min(f,e.c)); // 繼續搜 能流過的最大流量 if(d>0) { // 說明可流過d流量 e.c-=d; //
那麽這條邊的剩余容量減小 E[e.v][e.r].c+=d; // 反向邊剩余容量變大 return d; } } } return 0; } int maxFlow(int s,int t) { int flow=0; while(1) { memset(vis,0,sizeof(vis)); int f=dfs(s,t,INF); // 邊的容量改變後 繼續搜 // 邊的容量改變後就可能會走反向邊 // 走反向邊意味著 反悔正向的流量(可能有浪費) // 即 把正向的流量調小 if(f==0) return flow; // 直到不可流 flow+=f; } } /***/ /* 0~n-1 食物一側的牛 n~2*n-1 飲料一側的牛 2*n~2*n+f-1 食物 2*n+f~2*n+f+d-1 飲料 */ void solve() { // 增加一個源點 s=2*n+f+d // 增加一個匯點 t=s+1 int s=2*n+f+d, t=s+1; for(int i=0;i<=t;i++) E[i].clear(); for(int i=2*n;i<2*n+f;i++) addE(s,i,1); // 源點到食物 for(int i=2*n+f;i<2*n+f+d;i++) addE(i,t,1); // 飲料到匯點 for(int i=0;i<n;i++) { addE(i,i+n,1); // 一頭牛拆成的兩個點連邊 for(int j=0;j<f;j++) if(F[i][j]) addE(2*n+j,i,1); // 食物到牛的一點 for(int j=0;j<d;j++) if(D[i][j]) addE(n+i,2*n+f+j,1); // 另一點到飲料 } printf("%d\n",maxFlow(s,t)); } int main() { while(~scanf("%d%d%d",&n,&f,&d)) { memset(F,0,sizeof(F)); memset(D,0,sizeof(D)); for(int i=0;i<n;i++) { int a,b,t; scanf("%d%d",&a,&b); while(a--) { scanf("%d",&t); F[i][t-1]=1; } while(b--) { scanf("%d",&t); D[i][t-1]=1; } } solve(); } return 0; }
View Code

POJ 3281 /// 最大流