题意:
给定n个点和m条边, 每条边有流量上下限[b,c], 求是否存在一种流动方法使得每条边流量在范围内, 而且每个点的流入 = 流出
分析:
无源汇有上下界最大流模板, 记录每个点流的 in 和 out , 然后如果一个点 i 的in > out, 从源点i连一条边到in, out > in 就从i 连一条边到 v.
#include <cstdio> #include <iostream> #include <cstring> #include <queue> using namespace std; const int maxN = 1e5 + 7; const int maxM = 2e6 + 7; const int INF = 1e9 + 7; int n, m, S, T, ecnt; int in[maxN], out[maxN], B[maxN]; int head[maxN]; struct { int to, nxt, w; } edge[maxM]; void init() { memset(in, 0, sizeof(in)); memset(out, 0, sizeof(out)); memset(B, 0, sizeof(B)); ecnt = 0; memset(head, -1, sizeof(head)); } void addEdge(int u, int v, int w) { edge[ecnt].nxt = head[u]; edge[ecnt].to = v; edge[ecnt].w = w; head[u] = ecnt++; } int depth[maxN]; bool bfs() { memset(depth, -1, sizeof(depth)); queue<int> q; depth[S] = 0; q.push(S); while(!q.empty()) { int u = q.front(); q.pop(); for(int i = head[u]; i != -1; i = edge[i].nxt) { int v = edge[i].to, w = edge[i].w; if(w > 0 && depth[v] == -1) { //若该残量不为0,且V[i]还未分配深度,则给其分配深度并放入队列 depth[v] = depth[u] + 1; q.push(v); } } } if(depth[T] == -1) return false; return true;//当汇点的深度不存在时,说明不存在分层图,同时也说明不存在增广路 } int dfs(int u, int flow) { //u为当前节点 , flow为当前流量 if(u == T) //已经到达汇点, 直接返回 return flow; for(int i = head[u]; i != -1; i = edge[i].nxt) { int v = edge[i].to, w = edge[i].w; if((depth[v] == depth[u] + 1) && (w != 0)) { //注意这里要满足分层图和残量不为0两个条件 int di = dfs(v, min(flow, w)); if(di > 0) { edge[i].w -= di; edge[i ^ 1].w += di; //边是相反的两条, 奇数-1 偶数+1 return di; } } } return 0; //没有増广路 } int Dinic() { int ans = 0, d = 0; while(bfs()) { while(d = dfs(S, INF)) ans += d; } return ans; } int main() { // freopen("1.txt","r", stdin); ios::sync_with_stdio(false); int Test; cin >> Test; while(Test--) { cin >> n >> m; init(); S = n + 1, T = n + 2; for(int i = 0; i < m; i++) { int u, v, b, c; cin >> u >> v >> b >> c; addEdge(u,v,c - b); addEdge(v,u, 0); B[i] = b; out[u] += b;//记录入流 in[v] += b;// 记录出流 } int sum = 0; for(int i = 1; i <= n; i++) { //加边 int tmp = in[i] - out[i]; if(tmp > 0) { addEdge(S, i, tmp); addEdge(i, S, 0); sum += tmp; } else { addEdge(i, T, -tmp); addEdge(T, i, 0); } } int ans = Dinic(); if(ans == sum) { puts("YES"); for(int i = 0; i < m; i ++) printf("%d ",B[i] + edge[i * 2 + 1].w); //输出的是下限 + 反向边 }else{ puts("NO"); } puts(""); } }