最大流水题
题意:给n个点从1到n标号,每个点有一个限制值a[i],表示从这个点流出的流量的最大值,就算有超过a[i]的流量流进i点,最后也只有a[i]流量从i点流出。然后每条有向边也有容量cap[u][v]
然后给你一个源点0,它会和一些顶点相连,这些边的容量是无穷大的,然后给你一个汇点n+1,也会和一些顶点相连,这些边的容量也是无穷大的,
然后求源点到汇点的最大流
想了一下,有思路,无非是处理一下边的容量,起初想是对于有向边 u---->v cap[u][v]=min( cap[u][v] , a[u]); 处理完就直接最大流模板上去,过了sample,提交WA,后来才想到是
cap[u][v]=min( cap[u][v] , a[u] , a[v]); 提交WA………………才想起来,那些无穷大的边的容量没有做处理,和源点关联的边的容量由无穷大改为与之相连的那个点的限制值a[v] ,与汇点关联的那些边的容量改为那个点的限制值a[u],然后最大流模板直接上去,AC了,水题遂过
然后查了一下网上的解题报告,说是要拆成两点,然后连一条边,容量就是限制值,拆点的代码这里没给出,下次再写了
#include <cstdio> #include <cstring> #include <queue> using namespace std; #define N 110 #define INF 0x3f3f3f3f int cap[N][N]; int n,m,numb,numd; //源点和汇点 int a[N]; //a是每个顶点的限制量 void input() { memset(cap,0,sizeof(cap)); for(int i=1; i<=n; i++) scanf("%d",&a[i]); scanf("%d",&m); for(int i=1; i<=m; i++) { int u,v,w; scanf("%d%d%d",&u,&v,&w); cap[u][v]+=w; if(cap[u][v]>a[u]) cap[u][v]=a[u]; if(cap[u][v]>a[v]) cap[u][v]=a[v]; } scanf("%d%d",&numb,&numd); for(int i=1; i<=numb; i++) { int v; scanf("%d",&v); cap[0][v]=a[v]; } for(int i=1; i<=numd; i++) { int u; scanf("%d",&u); cap[u][n+1]=a[u]; } return ; } void print_graph() { for(int i=0; i<=n+1; i++) { for(int j=0; j<=n+1; j++) printf("%d ",cap[i][j]); printf("\n"); } return ; } void EK() { int F=0; //记录最终最大流 int min[N]; //记录最小残余流量 int p[N]; //记录路径 int flow[N][N]; //流量 memset(flow,0,sizeof(flow)); while(1) { memset(min,0,sizeof(min)); min[0]=INF; //递推数组,每次增广路找最小值 memset(p,-1,sizeof(p)); queue <int> q; q.push(0); while(!q.empty()) //BFS { int u=q.front(); q.pop(); for(int v=0; v<=n+1; v++) if( !min[v] && cap[u][v]>flow[u][v]) { p[v]=u; q.push(v); min[v]=min[u]<(cap[u][v]-flow[u][v]) ? min[u] : (cap[u][v]-flow[u][v]); } } if(!min[n+1]) break; for(int u=n+1; u!=0; u=p[u]) //增广 { flow[p[u]][u]+=min[n+1]; flow[u][p[u]]-=min[n+1]; } F+=min[n+1]; } printf("%d\n",F); } int main() { while(scanf("%d",&n)!=EOF) { input(); //print_graph(); EK(); //EK算法求最大流 } return 0; }