POJ 3436 ACM Computer Factory 【网络流】【北大ACM/ICPC竞赛训练】

我ac掉网络流的第一题!

先总结一下网络流的一些算法吧,首先是Ford-Fulkerson算法,这个算法是保证了众多网络流算法的【正确性】,其他算法也是基于其【优化】得到的。Ford的算法在于引入“反向边”的概念,反向边就是反悔边,代表你给修正以前走了的边一个机会。为什么反向边是对的呢,凭空加进来一条边真的大丈夫吗,关于这个有相关正确性的证明,我也说不清楚只能直觉上去理解。

之后是Edmonds-Karp即最短增广路算法,顾名思义,每次都找到达汇点边数最少的增广路,由此避免一些特定的消耗时间的情况。

然后是我现在用的Dinic算法,它的优化在于之前的两次算法都是每次找一条增广路,但实际上我们可以一次找【多条增广路】,所以手工写deque加回溯又可以进一步缩小时间复杂度。

 

一道网络流的题出的好不好,在于一眼能不能让选手看出是网络流模型。这道题我觉得出的蛮好的,把每个machine的input和output当作点去建边,就可以了。(input编号就是机器编号,output编号是其input编号加n,这样就可以把每个点标示出来了,其中一个input到另一个input或output到另一个output没有意义,所以就是0)

  1 #include<iostream>
  2 #include<cmath>
  3 #include<vector>
  4 #include<queue>
  5 #include<deque>
  6 #include<cstring>
  7 #define INF 1000000000
  8 using namespace std;
  9 
 10 int n,p;
 11 struct node{
 12     int in[12],out[12];
 13     int flow;
 14     node(int f1=0): flow(f1) {
 15         memset(in,0,sizeof(in));
 16         memset(out,0,sizeof(out));
 17     }
 18 }machine[110];
 19 int G[105][105],G2[105][105],layer[110];//G2是残余网络
 20 struct node1{
 21     int from,to,vol;
 22     node1(int i1=0,int j1=0,int v1=0): from(i1),to(j1),vol(v1) {}
 23 };
 24 vector<node1> ans;
 25 
 26 bool ok(int a,int b){//从a的output输送到b的input 
 27     for(int i=1;i<=p;i++){
 28         if( machine[a].out[i]==machine[b].in[i] || machine[b].in[i]==2 ) continue;
 29         return false;
 30     }
 31     return true;
 32 }
 33 
 34 bool check0(int a){//源点到a机器的input可不可以
 35     for(int i=1;i<=p;i++){
 36         if( machine[a].in[i]==1 ) return false;
 37     }
 38     return true;
 39 }
 40 
 41 bool check1(int a){//a机器的output到汇点可不可以
 42     for(int i=1;i<=p;i++){
 43         if( machine[a].out[i]!=1 ) return false;
 44     }
 45     return true;
 46 }
 47 
 48 int vis[1005];
 49 bool count_floor(){
 50     memset(layer,-1,sizeof(layer));
 51     layer[0]=0;
 52     queue<int> q;//int记录现在在的节点位置
 53     q.push(0);
 54     while(!q.empty()){
 55         int u = q.front(); q.pop();
 56         if(u==2*n+1) return true;
 57         for(int i=0;i<=2*n+1;i++){//枚举所有节点 
 58             if( G2[u][i]>0 && layer[i]==-1 ){
 59                 layer[i]=layer[u]+1;
 60                 q.push(i);
 61             }
 62         }
 63     } 
 64     return false;//搜不到汇点了 
 65 }
 66 
 67 int dinic(){
 68     //源点是0
 69     int maxflow=0;
 70     deque<int> s;//int记录目前dfs到的节点
 71     while( count_floor() ){
 72         s.push_back(0);
 73         memset(vis,0,sizeof(vis)); vis[0]=1;
 74         while(!s.empty()){
 75             int u = s.back();//先不急着pop_back掉
 76             if( u==2*n+1 ){//找到一条增广路径 
 77                 int minflow=INF,min_u;
 78                 for(int i=1;i<s.size();i++){
 79                     int u1=s[i-1],v1=s[i];
 80                     if( G2[u1][v1]<minflow ){ minflow=G2[u1][v1]; min_u=u1; }
 81                 }//找到一路上最细的管道
 82                 maxflow+=minflow;
 83                 for(int i=1;i<s.size();i++){
 84                     int u1=s[i-1],v1=s[i];
 85                     G2[u1][v1]-=minflow;//减掉原边
 86                     G2[v1][u1]+=minflow;//加上反向边 
 87                 }
 88                 while(!s.empty() && s.back()!=min_u) {
 89                     vis[ s.back() ] = 0;
 90                     s.pop_back();
 91                 }
 92             }//都结束了,自动回溯到最上面的点u使得u以上的树边flow不为0
 93             else{//到了一个点,向下dfs
 94                 int i;
 95                 for(i=0;i<=2*n+1;i++){
 96                     if( G2[u][i]>0 && layer[i]==layer[u]+1 && !vis[i] ){//每一次只往下一层走
 97                         vis[i]=1;
 98                         s.push_back(i);
 99                         break;
100                     }    
101                 }
102                 if(i==2*n+2) s.pop_back(); 
103             }     
104         } 
105     }
106      
107     return maxflow;
108 }
109 
110 
111 int main(){
112     
113     while(scanf("%d%d",&p,&n)!=EOF){
114         for(int i=1;i<=n;i++){
115             cin>>machine[i].flow;
116             for(int j=1;j<=p;j++) cin>>machine[i].in[j];
117             for(int j=1;j<=p;j++) cin>>machine[i].out[j];    
118         }
119     
120         //开始建图
121         for(int i=1;i<=n;i++) G2[i][i+n]=G[i][i+n]=machine[i].flow;//从n的input到n的output 
122         //output代表的节点是机器的编号+n
123         //源点是0,汇点是2n+1
124         for(int i=1;i<=n;i++){
125             if( check0(i) ) G2[0][i]=G[0][i]=INF;
126             if( check1(i) ) G2[i+n][2*n+1]=G[i+n][2*n+1]=INF;
127         } 
128     
129         //建每个机器的output能不能到其他机器的input
130         for(int i=1;i<=n;i++){
131             for(int j=1;j<=n;j++){
132                 if(i==j) continue;
133                 if( ok(i,j) ) G2[i+n][j]=G[i+n][j]=INF;
134             }
135         }
136     
137         cout<<dinic()<<" ";
138         for(int i=1;i<=n;i++){
139             for(int j=1;j<=n;j++){
140                 if(i==j) continue;
141                 if( ok(i,j) && G[i+n][j]-G2[i+n][j]>0 ) ans.push_back( node1(i,j,G[i+n][j]-G2[i+n][j]) );
142             }
143         } 
144         cout<<ans.size()<<endl;
145         for(int i=0;i<ans.size();i++) cout<<ans[i].from<<" "<<ans[i].to<<" "<<ans[i].vol<<endl;
146         ans.clear();
147     }
148     
149     return 0;    
150 }

 

转载于:https://www.cnblogs.com/ZhenghangHu/p/9410691.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值