求最小割的问题。
题意:已知网络中有n个源点,m的中转站(也就是节点),一个汇点(编号为0)。给出网络,求一些边(增大这个边就可以增大汇点流量的边)。
思路:一开始代码只找了有流=0就加入输出数组的情况,然而忽略了流向一条S->T的流有多个边权=0的情况,此时只增大一条边的值是没用的。
所以除了用一次最大流算法后,还需要用两次dfs分别从超级源点S和汇点T开始搜索,这样就可以把有多个0-0-0和单个边权为0的情况判断出来。
1 #include <cstdio>
2 #include <cstring>
3 #include <cstdlib>
4 #include <cmath>
5 #include <queue>
6 #include <stack>
7 #include <map>
8 #include <set>
9 #include <vector>
10 #include <algorithm>
11 using namespace std;
12 const int INF = 0x3f3f3f3f;
13 typedef struct node
14 {
15 int u;
16 int v;
17 int Flow;
18 int next;
19 }Line;
20 Line Li[2200];
21 int Head[110],top;
22 int vis[110],ans[110];
23 bool vis1[110],vis2[110];
24 int n,m,l;
25 int s,t;
26 void AddEdge(int u,int v,int f)
27 {
28 Li[top].v=v; Li[top].u=u;
29 Li[top].Flow=f;
30 Li[top].next=Head[u];
31 Head[u]=top++;
32 }
33 bool BFS()
34 {
35 memset(vis,-1,sizeof(vis));
36 vis[s]=0;
37 queue<int >Q;
38 Q.push(s);
39 while(!Q.empty())
40 {
41 int u=Q.front();
42 Q.pop();
43 for(int i=Head[u];i!=-1;i=Li[i].next)
44 {
45 if(Li[i].Flow&&vis[Li[i].v]==-1)
46 {
47 vis[Li[i].v]=vis[u]+1;
48 Q.push(Li[i].v);
49 }
50 }
51 }
52 return vis[t]!=-1;
53 }
54 int DFS(int u,int f)
55 {
56 if(u==t)
57 {
58 return f;
59 }
60 int ans=0;
61 for(int i=Head[u];i!=-1;i=Li[i].next)
62 {
63 if(Li[i].Flow&&vis[Li[i].v]==vis[u]+1)
64 {
65 int d=DFS(Li[i].v,min(f,Li[i].Flow));
66 f-=d;
67 Li[i].Flow-=d;
68 Li[i^1].Flow+=d;
69 ans+=d;
70 }
71 }
72 return ans;
73 }
74 void dfs(int u,bool *vist,int op)
75 {
76 vist[u]=true;
77 for(int i=Head[u];i!=-1;i=Li[i].next)
78 {
79 if(!vist[Li[i].v]&&Li[i^op].Flow!=0)
80 {
81 dfs(Li[i].v,vist,op);
82 }
83 }
84 }
85 void Dinic()//网络流进行增广
86 {
87 int ans;
88 while(BFS())
89 {
90 ans=DFS(s,INF);
91 printf("!
");
92 }
93 }
94 int main()
95 {
96 while(~scanf("%d %d %d",&n,&m,&l))
97 {
98 if(n+m+l==0)
99 {
100 break;
101 }
102 s=n+m+1;//源点
103 t=0;//汇点
104 memset(Head,-1,sizeof(Head));
105 int a,b,c;
106 top = 0;
107 for(int i=0;i<l;i++)
108 {
109 scanf("%d %d %d",&a,&b,&c);
110 AddEdge(a,b,c);//建立边,正向为c,负向为0
111 AddEdge(b,a,0);
112 }
113 for(int i=1;i<=n;i++)
114 {
115 AddEdge(s,i,INF);
116 AddEdge(i,s,0);//建立城市与源点之间的边,权值为INF
117 }
118 Dinic();
119 memset(vis1,false,sizeof(vis1));
120 memset(vis2,false,sizeof(vis2));
121 dfs(s,vis1,0);//从源点向汇点搜索,标记还有剩余流的点
122 dfs(t,vis2,1);//从汇点到源点搜索,标记还有剩余流的点
123 int num=0;
124 for(int i=0;i<l;i++)
125 {
126 if(Li[i<<1].Flow==0&&vis1[Li[i<<1].u]&&vis2[Li[i<<1].v])
127 ans[num++]=i+1;//如果一条边的u与v都被标记,表明s->u,v->t,但是这条边是满流,所以提升这条边。
128 }
129 if(num)
130 {
131 for(int i=0;i<num;i++)
132 {
133 if(i)printf(" ");
134 printf("%d",ans[i]);
135 }
136 }
137 printf("
");
138 }
139 return 0;
140 }
代码转自(https://blog.csdn.net/huayunhualuo/article/details/50554800)这个题解的板子跟我的差不多就转了