我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
时间: 2024-10-08 15:16:10