POJ 3436 ACM Computer Factory(最大流 残留网络找边)
坑爹啊!!!!!!!
这题弄了我一天啊!到最后在样例都没过的情况下,怀着无语的心情蛮交了一次,竟然过了!这不是坑爹是什么!
不过分析了一下,这样的情况应该也是算对的!因为增广路可能的情况本身就有许多种,所以样例不过的情况也是允许的吧!
这题的意思其实有点乱,不过梳理一下就比较容易理解,首相想到是网络流的题目比较有困难,然后就是题意了。具体是,以“工厂”作为节点,intput 全为2是与源点连通,制造一个0为源点,2*n+1为汇点。。
剩下的把每个机器(节点)分解成两个点(i,i+n),这两个点之间最大流量即为Performance。。
其他可用的边流量都是无穷大,记得1-n的点只能进,n+1-2*n的点只能出。。。
例如:机器i的完成品可用到j上加工 maxflow[i+n][j]=MAXINT。。。祝大家最大流愉快!
附上代码,其实主要是我一直想使用自己的Dinic模板,不想换,所以坑了我一整天。
#include <iostream> #include <stdio.h> #include <string.h> using namespace std; const int maxn=150000; const int maxm=200000; const int inf=1<<30; struct edge { int from,to,val,next; int flow; }map[maxn]; edge lex[500]; int vis[maxn],que[maxn],dist[maxn],len,n; int vised[maxn]; int pcnt=0; int T; void init() { len=0; memset(vis,-1,sizeof(vis)); } void insert (int from,int to,int val) { map[len].from=from; map[len].to=to; map[len].val=val; map[len].flow=val; map[len].next=vis[from]; vis[from]=len++; map[len].from=to; map[len].to=from; map[len].val=0; map[len].flow=0; map[len].next=vis[to]; vis[to]=len++; } int Dinic(int n,int s,int t) { int ans=0; while(true) { int head,tail,id,i; head=tail=0; que[tail++]=s; memset(dist,-1,sizeof(dist)); dist[s]=0; while(head<tail) { id=vis[que[head++]]; while(id!=-1) { if(map[id].val>0&&dist[map[id].to]==-1) { dist[map[id].to]=dist[map[id].from]+1; que[tail++]=map[id].to; if(map[id].to==t) { head=tail; break; } } id=map[id].next; } } if(dist[t]==-1) break; id=s,tail=0; while(true) { if(id==t) //找到一条增广路 { int flow=inf,fir; for(i=0;i<tail;i++) if(map[que[i]].val<flow) { fir=i; flow=map[que[i]].val; } for(i=0;i<tail;i++) map[que[i]].val-=flow,map[que[i]^1].val+=flow; ans+=flow; tail=fir; id=map[que[fir]].from; } id=vis[id]; while(id!=-1) { if(map[id].val>0&&dist[map[id].from]+1==dist[map[id].to]) break; id=map[id].next; } if(id!=-1) { que[tail++]=id; id=map[id].to; } else { if(tail==0) break; dist[map[que[tail-1]].to]=-1; id=map[que[--tail]].from; } } } return ans; } //以上依然是纯模板 int main() { int p,i,j,k; int num[51][101]; while(scanf("%d%d",&p,&n)!=EOF) { int flag,flag1; init(); memset(num,0,sizeof(num)); T=2*n+1; for(i=1;i<=n;i++) for(j=0;j<=2*p;j++) scanf("%d",&num[i][j]); //建图输入 num[i][0] 都未这个工厂的效率 for(i=1;i<=n;i++) { flag=1; flag1=1; insert(i,i+n,num[i][0]); for(j=1;j<=p;j++) { if(num[i][j]!=0&&num[i][j]!=2) //判断是否和源点相连 flag=0; if(num[i][j+p]!=1) //判断是否和汇点相连 flag1=0; } if(flag) insert(0,i,num[i][0]); //运用上面的判断是否建立边 if(flag1) insert(n+i,2*n+1,num[i][0]); for(j=1;j<=n;j++) { int kkk=1; for(k=1;k<=p;k++) if(j!=i) if(num[i][k+p]!=num[j][k]&&num[j][k]!=2) //判断前一个工厂的结果是否为下一个工厂的前提 { kkk=0; break; } if(kkk) { insert(i+n,j,inf); } } } memset(vised,0,sizeof(vised)); int ans=Dinic(T+1,0,T); pcnt=0; for(i=n+1;i<=2*n+1;i++) for(j=vis[i];j!=-1;j=map[j].next) if(map[j].to>0&&map[j].to<=n&&map[j].val<map[j].flow) //这个地方坑了我无限久,找残留网络中的点,不为0或者不为inf值就是需要的边 pcnt++; printf("%d %d\n",ans,pcnt); for(i=n+1;i<=2*n+1;i++) for(j=vis[i];j!=-1;j=map[j].next) //再来一次输出就好 if(map[j].to>0&&map[j].to<=n&&map[j].val<map[j].flow) cout<<i-n<<" "<<map[j].to<<" "<<map[j].flow-map[j].val<<endl; } return 0; }
依然希望大家自己去考虑考虑,模板什么都坚持使用一种!