【無源匯有上下界可行流】ZOJ
阿新 • • 發佈:2018-12-24
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;
}