網路流 拆點+最小割點集
P4662 [BalticOI 2008]黑手黨
題目描述
Byteland 國警方收到了一條匿名舉報,其中說當地黑幫老大正計劃一次從港口到郊區倉庫的運輸。警方知道運輸的時間並且知道運輸需要用到國家的高速公路網。
高速公路網包含雙向的高速公路段,每個路段直接連著兩個不同的收費站。一個收費站可能與很多其他的收費站相連。汽車只能通過收費站進入或離開高速公路網。據所知,黑幫會距港口邊最近的收費站進入高速公路,從距倉庫最近的收費站離開(不會在出高速後重新進入)。特警組位於選定的收費站處。當運輸車輛進入被監控的收費站時,它就會被警察抓住。
從這個角度看,最簡單的辦法就是在每個收費站處都安排特警班。然而,控制一個收費站需要特定的費用,每個收費站費用不同。警方想要讓花費最小,所以他們需要制定一個收費站的最小控制集,這個集合滿足兩個條件:
所有從港口到倉庫的交通必須至少經過集合中的一個收費站
監控這些收費站的費用(即監控每一個收費站費用之和)最小
你可以假設使用高速公路可以從港口到倉庫。
任務
寫一個程式能夠:
從標準輸入中讀取高速公路網,監控代價和運輸的起點和終點
找到收費站的最小控制集
輸出這個集合到標準輸出
輸入格式
標準輸入的第一行包含兩個整數 n 和 m,表示收費站的總數和公路段的總數。收費站按 1 到 n 標號;
第二行包含兩個整數 a 和 b,分別表示距港口和倉庫最近的兩個收費站編號;
接下來 n 行表示控制費用,第 i 行包含一個整數,表示第 i 個收費站的控制費用 c;
接下來 m 行表示高速公路網,第 j 行包含兩個整數 x 和 y,表示在 x 和 y 收費站之間有一條公路段相連。每一條高速公路段只出現一次。
輸出格式
唯一的一行輸出應包含最小控制集中收費站的編號,以遞增順序輸出,用一個空格分隔。
如果有多於一個最小控制集,你的程式可以輸出任意一個。
輸入
5 6
5 3
2
4
8
3
10
1 5
1 2
2 4
4 5
2 3
3 4
輸出
1 4
思路
拆點之後。把費用當流量,就是最小割。
我們在殘餘網路DFS。不經過滿流的邊,那麼如果一個點拆的2個點一個在S集合。一個在T集合。那麼這個點就是選擇的。因為源匯可以拆
我們應該建立虛的源匯。
#include<bits/stdc++.h> using namespace std; const int maxn = 2e5 + 10; const int maxm= 1e6+ 10; const int INF = 0x3f3f3f3f; //註釋為弧優化 struct max_Folw { int d[maxn], cur[maxn], start, tend; int Q[maxn * 2]; struct node { int to, cap, flow, next; } edge[maxm << 1]; int head[maxn]; bool vis[maxn]; int cnt; 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] = {to, cap, 0, 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 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; } //得到割點集合 queue<int> q; void bfs(){ memset(vis, 0, sizeof(vis)); vis[start]=1; q.push(start); while(!q.empty()){ int u=q.front(); q.pop(); for(int i = head[u]; i != -1; i = edge[i].next){ int to=edge[i].to, w=edge[i].cap-edge[i].flow; if(w&&!vis[to]){ vis[to]=1; q.push(to); } } } } }flow; //edge[i].cap-edge[i].flow==0 容量-流量=可流的流量 =0說明已經滿流 int a[maxn]; set<int> st; int main() { int n, m, A, B; scanf("%d%d%d%d",&n, &m, &A, &B); flow.init(0, 2*n+5); flow.AddEdge(0, A, INF); flow.AddEdge(B+n, 2*n+5, INF); for(int i=1; i<=n; i++){ scanf("%d", &a[i]); flow.AddEdge(i, i+n, a[i]); } for(int i=1; i<=m; i++){ int x, y; scanf("%d%d", &x, &y); flow.AddEdge(x+n, y, INF); flow.AddEdge(y+n, x, INF); } flow.maxflow(); flow.bfs(); for(int i=1; i<=n; i++){ if((flow.vis[i]&&!flow.vis[i+n])||(!flow.vis[i]&&flow.vis[i+n])){ st.insert(i); } } for(auto x: st){ printf("%d ", x); } printf("\n"); return 0; }