大意:给定一个图(多汇点,一个流入点),问能改变哪些边的流量使得原网络的流增加。
思路:求网络流里的关键边,关键边一是最小割集上的子集。
它具有如下性质:
1、满流;
2、源点可以通过残余网络(残余量不为0)访问到该边的起始点u;
3、汇点可以通过残余网络(残余量不为0)访问到该边的终点v;
求出网络流后,2次dfs求出所有标记即可,如果该边满流且起点、终点都被标记,则说明该边是关键边。
#include <iostream> #include <cstdlib> #include <cstdio> #include <string> #include <cstring> #include <cmath> #include <vector> #include <queue> #include <algorithm> #include <map> using namespace std; struct Edge { int from, to, cap, flow; Edge(int from, int to, int cap, int flow): from(from), to(to), cap(cap), flow(flow) {} }; const int maxn = 105; const int INF = 0x3f3f3f3f; struct ISAP { int n, m, s, t; vector<Edge> edges; vector<int> G[maxn]; bool vis[maxn]; int d[maxn]; int cur[maxn]; int p[maxn]; int num[maxn]; bool vis1[maxn], vis2[maxn]; void init(int n) { this->n = n; for(int i = 0; i < n; i++) G[i].clear(); edges.clear(); memset(vis1, 0, sizeof(vis1)); memset(vis2, 0, sizeof(vis2)); } void AddEdge(int from, int to, int cap) { edges.push_back(Edge (from, to, cap, 0)); edges.push_back(Edge (to, from, 0, 0)); m = edges.size(); G[from].push_back(m-2); G[to].push_back(m-1); } void ClearFlow() { for(int i = 0; i < edges.size(); i++) edges[i].flow = 0; } bool bfs() { memset(vis, 0, sizeof(vis)); queue<int> Q; Q.push(t); d[t] = 0; vis[t] = 1; while(!Q.empty()) { int x = Q.front(); Q.pop(); for(int i = 0; i < G[x].size(); i++) { Edge&e = edges[G[x][i]^1]; if(!vis[e.from] && e.cap > e.flow) { vis[e.from] = 1; d[e.from] = d[x] + 1; Q.push(e.from); } } } return vis[s]; } int Augment() { int x = t, a = INF; while(x != s) { Edge&e = edges[p[x]]; a = min(a, e.cap-e.flow); x = edges[p[x]].from; } x = t; while(x != s) { edges[p[x]].flow += a; edges[p[x]^1].flow -= a; x = edges[p[x]].from; } return a; } int MaxFlow(int s, int t) { this->s = s; this->t = t; int flow = 0; bfs(); memset(num, 0, sizeof(num)); for(int i = 0; i < n; i++) num[d[i]]++; int x = s; memset(cur, 0, sizeof(cur)); while(d[s] < n) { if(x == t) { flow += Augment(); x = s; } int ok = 0; for(int& i = cur[x]; i < G[x].size(); i++) { Edge&e = edges[G[x][i]]; if(e.cap > e.flow && d[x] == d[e.to]+1) { ok = 1; p[e.to] = G[x][i]; cur[x] = i; x = e.to; break; } } if(!ok) { int m = n-1; for(int i = 0; i < G[x].size(); i++) { Edge&e = edges[G[x][i]]; if(e.cap > e.flow) m = min(m, d[e.to]); } if(--num[d[x]] == 0) break; num[d[x] = m+1]++; cur[x] = 0; if(x != s) x = edges[p[x]].from; } } return flow; } void dfs1(int u) { vis1[u] = 1; for(int i = 0; i < G[u].size(); i++) { Edge&e = edges[G[u][i]]; if(!vis1[e.to] && e.cap - e.flow > 0) { dfs1(e.to); } } } void dfs2(int u) { vis2[u] = 1; for(int i = 0; i < G[u].size(); i++) { Edge &e = edges[G[u][i]^1]; if(!vis2[e.from] && e.cap > e.flow) { dfs2(e.from); } } } }; ISAP solver; int n, m, L; int s, t; int read_case() { scanf("%d%d%d", &n, &m, &L); if(!n) return 0; solver.init(n+m+3); for(int i = 0; i < L; i++) { int from, to, cap; scanf("%d%d%d", &from, &to, &cap); solver.AddEdge(from, to, cap); } s = n+m+1, t = 0; for(int i = 1; i <= n; i++) solver.AddEdge(s, i, INF); solver.MaxFlow(s, t); return 1; } void solve() { solver.dfs1(s); solver.dfs2(t); int first = 1; for(int i = 0; i < solver.m; i+=2) { Edge&e = solver.edges[i]; if(solver.vis1[e.from] && solver.vis2[e.to] && e.cap == e.flow) { if(first) { printf("%d", i/2+1); first = 0;} else printf(" %d", i/2+1); } } if(first) printf("\n"); printf("\n"); } int main() { while(read_case()) { solve(); } return 0; }