1. 程式人生 > >【無源匯有上下界可行流】ZOJ

【無源匯有上下界可行流】ZOJ

Step1 Problem:

給你 n 個點,m 條流量下界是 low ,上界是 up 的單向邊。問你能否滿足每時每刻每條邊的流量都在 [low, up] 範圍內,如果不滿足輸出 NO, 滿足輸出 YES 同時輸出每條邊的流量。

Step2 Ideas:

上下界網路流和平常的網路流不同在於多出了兩個點:超級源點 S, 超級匯點 T.
超級源點 S:每條邊下界流量都由 S 流出,這樣原圖就可以變成沒有下界的容量為 up-low 的圖了。
超級匯點 T:T 是用來判斷 流入每個點的流量 是否夠 它流出的下界
知道 S, T 的作用後:
例如:一條邊 u -> v,下界 low, 上界 up.
那麼 S -> v 流入 low,u -> T 流入 low,u-> v 流入 up-low. (這樣就沒有下界了)
S -> v 流入 low:S 充當了 u 流向 v 的下界
u -> T 流入 low:如果 u 有流量 low 流入 T,代表有流量 low 可以到達 u,那麼 u->v 的下界流量肯定能滿足


u-> v 流入 up-low:S 充當了 u 流向 v 的下界,所以我的上下界都得減少 low.

對於新圖跑 S 到 T 的最大流,如果 T 收到流量是滿流的 或者 S 發出去的流量是滿流的,就代表所有下界流量都能滿足。

Step3 Code:

#include<bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
const int M = 50000;
const int N = 500;
struct node
{
    int to, cap, next;
}Map[M];
int
head[N], cnt; int lsum[N], bl[N*N]; int vis[N], cur[N]; int ans[N*N]; bool bfs(int s, int e) { memset(vis, -1, sizeof(vis)); queue<int> q; q.push(s); vis[s] = 0; while(!q.empty()) { s = q.front(), q.pop(); for(int i = head[s]; ~i; i = Map[i].next) { int
to = Map[i].to, cap = Map[i].cap; if(cap && vis[to] == -1) { vis[to] = vis[s] + 1; q.push(to); } } } if(vis[e] == -1) return 0; else return 1; } int dfs(int s, int e, int f) { if(s == e) return f; int ans = 0; for(int &i = cur[s]; ~i; i = Map[i].next) { int to = Map[i].to, &cap = Map[i].cap; if(vis[to] > vis[s] && cap) { int d = dfs(to, e, min(f, cap)); if(d) { cap -= d; Map[i^1].cap += d; f -= d; ans += d; if(!f) break; } } } if(ans) return ans; vis[s] = -1; return 0; } int dinic(int s, int e) { int ans = 0; while(bfs(s, e)) { memcpy(cur, head, sizeof(head)); ans += dfs(s, e, inf); } return ans; } void init() { cnt = 0; memset(head, -1, sizeof(head)); memset(lsum, 0, sizeof(lsum)); memset(bl, 0, sizeof(bl)); } void add(int u, int v, int cap) { Map[cnt] = (node){v, cap, head[u]}; head[u] = cnt++; Map[cnt] = (node){u, 0, head[v]}; head[v] = cnt++; } int main() { int T, n, m, u, v, low, up; scanf("%d", &T); while(T--) { init(); scanf("%d %d", &n, &m); int S = 0, T = n+1; for(int i = 1; i <= m; i++) { scanf("%d %d %d %d", &u, &v, &low, &up); add(u, v, up-low); lsum[v] += low; //流入 v 的下界和 lsum[u] -= low; //流出 u 的下界和 bl[i] = low; } int sum = 0;// S 發出去的流量 for(int i = 1; i <= n; i++) { if(lsum[i] > 0) { sum += lsum[i]; add(S, i, lsum[i]);//流入比流出多 } if(lsum[i] < 0) add(i, T, -lsum[i]);//流出比流入多 } if(sum != dinic(S, T)) {//如果最大流不等於發出去的流量,則不滿足 printf("NO\n"); } else { printf("YES\n"); for(int i = 1; i <= m; i++) {//每條邊在下界的基礎上多流了多少流量 int id = (i-1)*2; ans[i] = Map[id^1].cap; } for(int i = 1; i <= m; i++) { printf("%d\n", ans[i]+bl[i]); } } printf("\n"); } return 0; }