[AHOI2009]MINCUT 最小割 邊存在最小割的唯一性
題目
(https://ac.nowcoder.com/acm/problem/19887)
題目描述
A,B兩個國家正在交戰,其中A國的物資運輸網中有N箇中轉站,M條單向道路。設其中第i (1 ≤ i ≤ M)條道路連線了vi,ui兩個中轉站,那麼中轉站vi可以通過該道路到達ui中轉站,如果切斷這條道路,需要代價ci。現在B國想找出一個路徑切斷方案,使中轉站s不能到達中轉站t,並且切斷路徑的代價之和最小。 小可可一眼就看出,這是一個求最小割的問題。但愛思考的小可可並不侷限於此。
現在他對每條單向道路提出兩個問題:
問題一:是否存在一個最小代價路徑切斷方案,其中該道路被切斷?
問題二:是否對任何一個最小代價路徑切斷方案,都有該道路被切斷?
現在請你回答這兩個問題。
輸入描述:
第一行有4個正整數,依次為N,M,s和t。
第2行到第(M+1)行每行3個正整數v,u,c表示v中轉站到u中轉站之間有單向道路相連,單向道路的起點是v,終點是u,切斷它的代價是c(1 ≤ c ≤ 100000)。
注意:兩個中轉站之間可能有多條道路直接相連。同一行相鄰兩數之間可能有一個或多個空格。
輸出描述:
對每條單向邊,按輸入順序,依次輸出一行,包含兩個非0即1的整數,分別表示對問題一和問題二的回答(其中輸出1表示是,輸出0表示否)。
同一行相鄰兩數之間用一個空格隔開,每行開頭和末尾沒有多餘空格。
輸入
6 7 1 6
1 2 3
1 3 2
2 4 4
2 5 1
3 5 5
4 6 2
5 6 3
輸出
1 0
1 0
0 0
1 0
0 0
1 0
1 0
思路
問題一:是否有一個最小割經過該邊
問題二:是否所有的最小割都經過該邊
結論題:
先跑最大流求出任意一個最小割
對殘量網路縮點,此時存在圖上的邊, 可能是滿流的邊,可能是和S不流通和T連的的點的0流邊
然後對於一條滿流的邊(u,v)
①Scc[u]!=Scc[v],存在(u,v)被割的方案
②Scc[u]Scc[S]&&Scc[v]Scc[T],(u,v)必定被割
#include<bits/stdc++.h> using namespace std; const int maxn = 4000 + 10; const int INF = 0x3f3f3f3f; //註釋為弧優化 struct node { int form, to, cap, flow, next; } edge[2000006]; int head[maxn]; int cnt; struct max_Folw { int d[maxn], cur[maxn], start, tend; bool vis[maxn]; void init(int s, int t) { memset(head, -1, sizeof(head)); cnt=0; start=s, tend=t; } void add(int start, int to, int cap) { edge[cnt].form = start; edge[cnt].to = to; edge[cnt].cap = cap; edge[cnt].flow = 0; edge[cnt].next = head[start]; head[start] = cnt++; } void AddEdge(int start, int to, int cap) { add(start, to, cap); add(to, start, 0); } bool BFS() { memset(d, -1, sizeof(d)); int Q[maxn * 2]; int Thead, Ttail; Thead = Ttail = 0; Q[Ttail++] = tend; d[tend] = 0; while (Thead<Ttail) { int x = Q[Thead]; if (x == start) return true; for (int i = head[x]; i != -1; i = edge[i].next) { int temp = edge[i].to; if (d[temp] == -1 && edge[i^1].cap > edge[i^1].flow) { //沒有標記,且可行流大於0 d[temp] = d[x] + 1; Q[Ttail++] = temp; } } Thead++; } return false;//匯點是否成功標號,也就是說是否找到增廣路 } int DFS(int x, int cap) { if (x == tend) return cap; int flow = 0, f; //for (int i = cur[x]; i != -1; i = edge[cur[x]=i].next) { for (int i = head[x]; i != -1; i = edge[i].next) { int temp = edge[i].to; if (d[temp] == d[x] - 1 && edge[i].cap > edge[i].flow) { f = DFS(temp, min(cap - flow, edge[i].cap - edge[i].flow)); edge[i].flow += f; edge[i ^ 1].flow -= f; flow += f; if (flow == cap) return flow; } } d[x] = -2;//防止重搜 return flow; } int maxflow() { int flow = 0, f; while (BFS()) { //memcpy(cur, head, sizeof head); flow += DFS(start, INF); } return flow; } } flow; int scc[maxn]; struct Tarjn{ int low[maxn]; int dfn[maxn]; int vis[maxn]; int T, N=0; stack<int> s; void tarjn(int u){ low[u]=dfn[u]=++T; s.push(u), vis[u]=1; for(int i=head[u]; i!=-1; i=edge[i].next){ int to=edge[i].to; if(edge[i].cap-edge[i].flow==0) continue;//滿流不能再訪問 if(!dfn[to]){//沒有訪問過 tarjn(to); low[u]=min(low[u], low[to]); } else if(vis[to]){//在棧中 low[u]=min(low[u], dfn[to]); } } if(low[u]==dfn[u]){ ++N; while(1){ int now=s.top(); s.pop(); vis[now]=0; scc[now]=N; if(now==u){ break; } } } } }ta; int main() { int n, m, s, t; scanf("%d%d%d%d", &n, &m, &s, &t); flow.init(s, t); for(int i=1; i<=m; i++) { int x, y, c; scanf("%d%d%d", &x, &y, &c); flow.AddEdge(x, y, c); } flow.maxflow(); for(int i=1; i<=n; i++){ if(!ta.dfn[i]){ ta.tarjn(i); } } for(int i=0; i<cnt; i+=2){ int x=edge[i].form, y=edge[i].to; //edge[i].cap-edge[i].flow==0 容量-流量=可流的流量 =0說明已經滿流 if(edge[i].cap-edge[i].flow==0&&scc[x]!=scc[y]){ printf("1 "); } else{ printf("0 "); } if(scc[x]==scc[s]&&scc[y]==scc[t]){ printf("1\n"); } else{ printf("0\n"); } } return 0; } /* 5 5 1 5 1 2 5 2 3 3 3 5 3 2 4 2 4 5 2 */