POJ1273
参考:http://www.cnblogs.com/jackge/archive/2013/04/10/3012182.html
裸的最大流题目,要注意重边。
int map[][] ,记录容量
int flow[][]记录流量
int res[]记录残余流量
int pre[]记录父亲节点,更新flow数组是用到
从流量为0开始找增广路,清空flow数组开始。
每次将残余流量数组清空,只有src源点的残余容量是无穷大。
用BFS寻找随机路径,并且同时更新res(残余流量数组)取最小值。
如果能更新到des,也就是 res[des]不等于0 ,则 说明找到了一条增广路,把它加到max_flow里;
然后更新整个网络,也就是flow数组。正流量增加 res[des],反向流量减少res[des];
不断重复上述过程,直到res[des] == 0,找不到增广路,也就是当前已经达到了最大流。(增广路定理)
#include <iostream> #include <queue> #include <string.h> #include <cstdio> #define N 250 #define INF 0x3f3f3f3f using namespace std; int map[N][N]; // 容量 int flow[N][N] ; //流量 int res[N];//每条边的残余流量 int pre[N]; //记录父亲节点的数组 //更新汇点的流量之后回溯时用到 int n,m,max_flow; int EK(int src,int des){ memset(flow,0,sizeof(flow)); // start from zero_flow; max_flow = 0; //set max_flow to zero queue<int>Q; while(1){ memset(res,0,sizeof(res)); // set remains to zero; res[src] = INF; // start point has infinite flow; Q.push(src); while(!Q.empty()){ int u = Q.front(); Q.pop(); for(int v=1;v<=m;v++){ if(!res[v] && map[u][v] > flow[u][v]){ pre[v] = u; Q.push(v); res[v] = min(res[u],map[u][v] - flow[u][v]); //如果res[u]不足,就将所有res //都流入res[v] ,否则 ,将res[v]流满,map[u][v] - flow[u][v]; } } } if(res[des] == 0) return max_flow; //找不到增广路 for(int v=des;v!=src;v=pre[v]){ flow[pre[v]][v] += res[des]; flow[v][pre[v]] -= res[des]; //更新反向弧 } max_flow += res[des]; } } int main(){ while(scanf("%d%d",&n,&m)!=EOF){ int from,to,w; memset(map,0,sizeof(map)); for(int i=0;i<n;i++){ scanf("%d%d%d",&from,&to,&w); map[from][to] += w; //重边 } int ans = EK(1,m); printf("%d\n",ans); } return 0; }
EK
处理多个源点可以通过增加一个超级源点,连接到多个源点。
同理增加一个超级汇点,让所有汇点连接到超级汇点。 可以参照算法竞赛入门经典。
#include <iostream> #include <queue> #include <string.h> #include <cstdio> #define N 110 #define INF 0x3f3f3f3f using namespace std; int map[N][N]; // 容量 int flow[N][N] ; //流量 int res[N];//每条边的残余流量 int pre[N]; //记录父亲节点的数组 //更新汇点的流量之后回溯时用到 int n,np,nc,m,max_flow; int src = 101,des=102; int EK(int src,int des){ memset(flow,0,sizeof(flow)); // start from zero_flow; max_flow = 0; //set max_flow to zero queue<int>Q; while(1){ memset(res,0,sizeof(res)); // set remains to zero; res[src] = INF; // start point has infinite flow; Q.push(src); while(!Q.empty()){ int u = Q.front(); Q.pop(); for(int v=0;v<=n+1;v++){ // 加入超级源点 汇点 n,n+1 if(!res[v] && map[u][v] > flow[u][v]){ pre[v] = u; Q.push(v); res[v] = min(res[u],map[u][v] - flow[u][v]); //如果res[u]不足,就将所有res //都流入res[v] ,否则 ,将res[v]流满,map[u][v] - flow[u][v]; } } } if(res[des] == 0) return max_flow; //找不到增广路 for(int v=des;v!=src;v=pre[v]){ flow[pre[v]][v] += res[des]; flow[v][pre[v]] -= res[des]; //更新反向弧 } max_flow += res[des]; } } int main(){ while(scanf("%d%d%d%d",&n,&np,&nc,&m)!=EOF){ char s[10]; memset(map,0,sizeof(map)); src = n,des = n+1; int from,to,w; char ch; for(int i=0;i<m;i++){ scanf(" %c%d%c%d%c%d",&ch,&from,&ch,&to,&ch,&w); map[from][to] = w; } for(int i=0;i<np;i++){ scanf(" %c%d%c%d",&ch,&from,&ch,&w); map[src][from] = w; //连接超级源点 } for(int i=0;i<nc;i++){ scanf(" %c%d%c%d",&ch,&from,&ch,&w); map[from][des] = w; //连接超级汇点 } int ans = EK(src,des); printf("%d\n",ans); } return 0; }
NYOJ677
做了这个题才知道最大流最小割定理是多么神奇,这个题就是裸求图的最小割,然后我们直接转化为最大流问题处理就行了。
关键在于构图,增添一个超级源点0,将超级源点与间谍所在节点连接,汇点是n。其余的结点相连的权值都设置为1。
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <set> #include <vector> #include <queue> #define INF 0x3f3f3f3f using namespace std; int spy[222]; int map[222][222]; int n,m,q; int res[222]; int flow[222][222]; int p[222]; int EK(int src,int des){ int max_flow = 0; memset(flow,0,sizeof(flow)); queue<int>Q; while(1){ memset(res,0,sizeof(res)); res[src] = INF; Q.push(src); while(!Q.empty()){ int u = Q.front();Q.pop(); for(int v = 0;v<=n;v++){ if(!res[v] && map[u][v] > flow[u][v]){ p[v] = u; res[v] = min(res[u] , map[u][v] - flow[u][v]); Q.push(v); } } } // cout<<res[des]<<endl; if(res[des] == 0) return max_flow; max_flow += res[des]; for(int v=des;v!=src;v=p[v]){ flow[p[v]][v] += res[des]; flow[v][p[v]] -= res[des]; } } } int main() { int T ; scanf("%d",&T); for(int cas=1;cas<=T;cas++){ scanf("%d%d%d",&n,&m,&q); int po; memset(map,0,sizeof(map)); for(int i=0;i<q;i++){ scanf("%d",&po); map[0][po] = INF; //超级源点 } int a,b; for(int i=0;i<m;i++){ scanf("%d%d",&a,&b); map[a][b] = map[b][a] = 1; } int ans = EK(0,n); printf("Case #%d: %d\n",cas,ans); } return 0; }