SGU 194 Reactor Cooling (无源汇有上下界的最大流)
Description
给出一个\(N\)个点\(M\)条边的循环网络,每条边有一个流量下限和上限,问是否有满足容量限制的最大流,若有,输出每条边上的流量。
Input
第一行给出结点数\(N\)和边数\(M\),接下来\(M\)行,每行给出四个数\(u\),\(v\),\(l\),\(c\),表示\(u\)到\(v\)有一条下限为\(l\)上限为\(c\)的边。\(1 \leqslant N \leqslant 200\)。
Output
如果有可行的最大流,输出"YES",接下来\(M\)行,每行一个整数,输出每条边上的流量,按输入的顺序给出。如果没有,输出"NO"。
Sample Input
4 6
1 2 1 3
2 3 1 3
3 4 1 3
4 1 1 3
1 3 1 3
4 2 1 3
Sample Output
YES
1
2
3
2
1
1
Solution
无源汇带上下界的最大流问题。转化为没有下限的网络流问题,对于原图中一条下限为\(l\)上限为\(c\)的边,建一条下限为\(0\),上限为\(c-l\)的边。对于每个点\(i\),令\(du[i]=\)流入i的所有下限之和-流出i的所有下限之和,建立源点\(s\)和汇点\(t\),对于每个结点\(i\),如果\(du[i]>0\),建一条从\(s\)到\(i\)的容量为\(du[i]\)的边,如果\(du[i]<0\),建一条从\(i\)到\(t\)的容量为\(du[i]\)的边,从\(s\)到\(t\)跑一遍最大流,如果满流,则说明有从可行的最大流,反之没有。
每条边上的真实流量=这条边上的流量下界+新图中边上的流量。新图中边上的流量刚好记录再这条边的反向弧中。
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long ll;
const int INF = 0x7f7f7f7f;
const int N = 200 + 10;
const int M = 4e4 + 10;
struct Edge
{
int to, c, next;
Edge() {}
Edge(int to, int c, int next) : to(to), c(c), next(next) {}
} edge[M];
int adj[N], tot;
void init()
{
memset(adj, -1, sizeof(adj));
tot = 0;
}
void add(int u, int v, int c)
{
edge[tot] = Edge(v, c, adj[u]);
adj[u] = tot++;
edge[tot] = Edge(u, 0, adj[v]);
adj[v] = tot++;
}
int level[N];
queue<int> q;
bool bfs(int s, int t)
{
while (!q.empty()) q.pop();
memset(level, -1, sizeof(level));
level[s] = 0; q.push(s);
while (!q.empty())
{
int u = q.front(); q.pop();
for (int i = adj[u]; i != -1; i = edge[i].next)
{
Edge &e = edge[i];
if (e.c && level[e.to] == -1)
{
level[e.to] = level[u] + 1;
if (e.to == t) return true;
q.push(e.to);
}
}
}
return false;
}
int cur[N];
int dfs(int u, int t, int flow)
{
if (u == t) return flow;
for (int &i = cur[u]; i != -1; i = edge[i].next)
{
Edge &e = edge[i];
if (e.c && level[e.to] > level[u])
{
int f = dfs(e.to, t, min(flow, e.c));
if (f)
{
e.c -= f;
edge[i ^ 1].c += f;
return f;
}
}
}
return 0;
}
int dinic(int s, int t)
{
int flow = 0;
while (bfs(s, t))
{
memcpy(cur, adj, sizeof(adj));
int f;
while (f = dfs(s, t, INF)) flow += f;
}
return flow;
}
int du[N], down[N];
int main()
{
int n, m;
scanf("%d%d", &n, &m);
init();
int s = 0, t = n + 1;
memset(du, 0, sizeof(du));
for (int i = 0; i < m; i++)
{
int u, v, b, c;
scanf("%d%d%d%d", &u, &v, &b, &c);
add(u, v, c - b);
du[u] -= b; du[v] += b;
down[i] = b;
}
int sum = 0;
for (int i = 1; i <= n; i++)
{
if (du[i] > 0) add(s, i, du[i]), sum += du[i];
else if (du[i] < 0) add(i, t, -du[i]);
}
if (dinic(s, t) < sum) return printf("NO\n"), 0;
printf("YES\n");
for (int i = 0; i < m; i++) printf("%d\n", down[i] + edge[2 * i + 1].c);
return 0;
}