题意:给定一个无源汇网络,求一个可行流,保证每个点的流量守恒。(有上下界的)
思路:这是个无源汇上下界可行流问题,可以转化成单纯最大流来做,具体理论证明见论文:一种简易的方法求解流量有上下界的网络中网络流问题
论文里讲的还是蛮好的,这里就讲下求解过程:
我们用B(u, v)来表示边(u, v)的下界,C(u, v)来表示边(u, v)的容量,则流量满足:B(u, v) <= f(u, v) <= C(u, v)
对于原图,更改容量,C'(u, v) = C(u, v) - B(u, v)
同时,增设源点S,汇点T
对于每个点,如果M( i ) > 0,则建边<S, i, M( i )>,如果M(i) < 0,则建边< i, T, -M( i )>
对新图求一次S-->T最大流,如果与S, T相连的边全部满流,则原图中存在一个可行流,保证每个点的流量守恒。
最后就可以求的每条边的流量:F(u, v) = f(u, v) + B(u, v)
代码
1 #include <cstdlib>
2 #include<cstdio>
3 #include<iostream>
4 using namespace std;
5 #define NN 205
6 #define MM 80810
7 #define INF 0x3fffffff
8
9 typedef struct node{
10 int v, w;
11 struct node *nxt, *op;
12 }NODE;
13 NODE edg[MM]; // 保存所有的边
14 NODE *Link[NN]; // 记录节点所在链表的首节点
15 int h[NN]; // 距离标号,记录每个点到汇点的距离,这里的距离指的是层数
16 int num[NN]; // gap优化,标号为i的顶点个数
17 /*
18 *
19 */
20
21 int M, N, idx, S, T, n; // S 表示源点,T表示汇点,n表示节点个数
22 int in[NN];
23 int out[NN];
24 int b[MM / 2];
25 void Add(int u, int v, int c1, int c2){
26 idx++; //idx记得初始化,不然很容易栈溢出
27 edg[idx].v = v;
28 edg[idx].w = c1;
29 edg[idx].nxt = Link[u];
30 edg[idx].op = edg + idx + 1;
31 Link[u] = edg + idx;
32 idx++;
33 edg[idx].v = u;
34 edg[idx].w = c2; // 有向边为0,无向边为c1
35 edg[idx].nxt = Link[v];
36 edg[idx].op = edg + idx - 1;
37 Link[v] = edg + idx;
38 }
39
40 int Min(int a, int b){
41 return a < b ? a : b;
42 }
43
44 int aug(int u, int flow){
45 if (u == T) return flow;
46 int l = flow; // l表示剩余容量
47 int tmp = n - 1;
48 for (NODE *p = Link[u]; p; p = p->nxt){
49 if (h[u] == h[p->v] + 1 && p->w){
50 int f = aug(p->v, Min(l, p->w));
51 l -= f;
52 p->w -= f;
53 p->op->w += f;
54 if (l == 0 || h[S] == n) return flow - l; // gap
55 }
56 if (p->w > 0 && h[p->v] < tmp){
57 tmp = h[p->v];
58 }
59 }
60 if(l == flow){// 如果没有找到增流,才修改标号,刚开始写错了,也杯具的过了好多题
61 num[h[u]]--; // gap
62 if (num[h[u]] == 0) h[S] = n; // gap,每个点的距离值最多为n - 1,这里设为n 表示断层了
63 else{
64 h[u] = tmp + 1;
65 num[h[u]]++; // gap
66 }
67 }
68 return flow - l;
69 }
70
71 void Init(){
72 idx = 0;
73 S = 0;
74 T = N + 1;
75 n = T + 1;
76 memset(Link, 0, sizeof(Link));
77 }
78 /*n表示总点的个数,包括源点和汇点*/
79 int sap(){
80 int ans = 0;
81 memset(h, 0, sizeof(h)); // h 保存的是距离标号(到汇点的)
82 memset(num, 0, sizeof(num));
83 num[0] = n;
84 while(h[S] < n){
85 ans += aug(S, INF);
86 }
87 return ans;
88 }
89
90 void Work(){
91 sap();
92 for (NODE *p = Link[S]; p; p = p->nxt){
93 if(p->w){
94 puts("NO");
95 return ;
96 }
97 }
98 for (NODE *p = Link[T]; p; p = p->nxt){
99 if(p->w != out[p->v] - in[p->v]){
100 puts("NO");
101 return ;
102 }
103 }
104 puts("YES");
105 int i, ans;
106 for (i = 1; i <= M; i++){
107 ans = b[i] + edg[i * 2].w;
108 printf("%d\n", ans);
109 }
110 }
111
112 int main(int argc, char** argv) {
113 int i, u, v, c;
114 scanf("%d%d", &N, &M);
115 fill(in, in + N, 0);
116 fill(out, out + N, 0);
117 Init();
118 for (i = 1; i <= M; i++){
119 scanf("%d%d%d%d", &u, &v, &b[i], &c);
120 Add(u, v, c - b[i], 0);
121 in[v] += b[i];
122 out[u] += b[i];
123 }
124 for (i = 1; i <= N; i++){
125 if(in[i] > out[i]) Add(S, i, in[i] - out[i], 0);
126 else Add(i, T, out[i] - in[i], 0);
127 }
128 Work();
129 return 0;
130 }
131
132