1 /* 2 首先建立一个源S和一个汇T,一般称为附加源和附加汇。 3 对于图中的每条弧<u,v>,假设它容量上界为c,下界b,那么把这条边拆为三条只有上界的弧。 4 一条为<S,v>,容量为b; 5 一条为<u,T>,容量为b; 6 一条为<u,v>,容量为c-b。 7 其中前两条弧一般称为附加弧。 8 然后对这张图跑最大流,以S为源,以T为汇,如果所有的附加弧都满流,则原图有可行流;否则就是无解。 9 这时,每条非附加弧的流量加上它的容量下界,就是原图中这条弧应该有的流量。 10 11 对于原图中的每条弧,我们把c-b称为它的自由流量,意思就是只要它流满了下界,这些流多少都没问题。 12 既然如此,对于每条弧<u,v>,我们强制给v提供b单位的流量,并且强制从u那里拿走b单位的流量,这一步对应着两条附加弧。 13 如果这一系列强制操作能完成的话,也就是有一组可行流了。 14 注意:这张图的最大流只是对应着原图的一组可行流,而不是原图的最大或最小流。 15 */ 16 #include <bits/stdc++.h> 17 using namespace std; 18 const int oo = (1LL << 31) - 1; 19 const int LEN = 1e5 + 5; 20 struct node { 21 int x, y, l, r; 22 } a[LEN]; 23 namespace ISAP { 24 int flow, tot, n, m, src, tar, qh, qt, cnt, ans; 25 struct edge { 26 int vet, next, len; 27 } E[LEN * 2]; 28 int dis[LEN], gap[LEN], head[LEN], cur[LEN], q[LEN], vis[LEN], IN[LEN]; 29 void add(int u, int v, int c) { 30 E[++tot] = (edge){v, head[u], c}; 31 head[u] = tot; 32 } 33 void join(int u, int v, int c) { 34 add(u, v, c); 35 add(v, u, 0); 36 } 37 void bfs(int s) { 38 qh = qt = 0; 39 q[++qt] = s; 40 dis[s] = 0; 41 vis[s] = 1; 42 while (qh < qt) { 43 int u = q[++qh]; 44 gap[dis[u]]++; 45 for (int e = head[u]; e != -1; e = E[e].next) { 46 int v = E[e].vet; 47 if (E[e ^ 1].len && !vis[v]) { 48 dis[v] = dis[u] + 1; 49 vis[v] = 1; 50 q[++qt] = v; 51 } 52 } 53 } 54 } 55 int isap(int u, int aug) { 56 if (u == tar) return aug; 57 int flow = 0; 58 for (int e = head[u]; e != -1; e = E[e].next) { 59 int v = E[e].vet; 60 if (E[e].len && dis[v] == dis[u] - 1) { 61 int tmp = isap(v, min(aug - flow, E[e].len)); 62 E[e].len -= tmp; 63 E[e ^ 1].len += tmp; 64 flow += tmp; 65 head[u] = e; 66 if (flow == aug || dis[src] == cnt) return flow; 67 } 68 } 69 if (!--gap[dis[u]++]) dis[src] = cnt; 70 ++gap[dis[u]]; 71 head[u] = cur[u]; 72 return flow; 73 } 74 void init() { 75 tot = -1, gap[0] = 0; 76 for (int i = 1; i <= cnt; i++) { 77 dis[i] = gap[i] = vis[i] = IN[i] = 0; 78 head[i] = -1; 79 } 80 } 81 int maxflow(int s, int t) { 82 src = s, tar = t; 83 int res = 0; 84 for (int i = 1; i <= cnt; i++) cur[i] = head[i]; 85 bfs(tar); 86 while (dis[src] < cnt) res += isap(src, oo); 87 return res; 88 } 89 } 90 using namespace ISAP; 91 int main() { 92 scanf("%d %d", &n, &m); 93 cnt = n; 94 src = ++cnt, tar = ++cnt; 95 init(); 96 for (int i = 1; i <= m; i++) { 97 int x, y, l, r; 98 scanf("%d %d %d %d", &x, &y, &l, &r); 99 a[i] = (node){x, y, l, r}; 100 join(x, y, r - l); 101 IN[y] += l, IN[x] -= l; 102 } 103 for (int i = 1; i <= n; i++) { 104 if (IN[i] < 0) join(i, tar, -IN[i]); 105 else { 106 join(src, i, IN[i]); 107 flow += IN[i]; 108 } 109 } 110 int ans = maxflow(src, tar); 111 if (flow == ans) { 112 puts("YES"); 113 for (int i = 1; i <= m; i++) printf("%d ", a[i].l + E[i * 2 - 1].len); 114 } else puts("NO"); 115 return 0; 116 }
1 /* 2 先来看有源汇可行流 3 建模方法: 4 建立弧<t,s>,容量下界为0,上界为oo。 5 然后对这个新图(实际上只是比原图多了一条边)按照无源汇可行流的方法建模, 6 如果所有附加弧满流,则存在可行流。 7 求原图中每条边对应的实际流量的方法,同无源汇可行流,只是忽略掉弧<t,s>就好。 8 而且这时候弧<t,s>的流量就是原图的总流量。 9 理解方法: 10 有源汇相比无源汇的不同就在于,源和汇是不满足流量平衡的,那么连接<t,s>之后, 11 源和汇也满足了流量平衡,就可以直接按照无源汇的方式建模。 12 注意:这张图的最大流只是对应着原图的一组可行流,而不是原图的最大或最小流。 13 14 有源汇最大流 15 建模方法: 16 首先按照有源汇可行流的方法建模,如果不存在可行流,更别提什么最大流了。 17 如果存在可行流,那么在运行过有源汇可行流的图上(就是已经存在流量的那张图,流量不要清零), 18 跑一遍从s到t的最大流(这里的s和t是原图的源和汇,不是附加源和附加汇),就是原图的最大流。 19 理解方法: 20 为什么要在那个已经有了流量的图上跑最大流?因为那张图保证了每条弧的容量下界,在这张图上跑最大流, 21 实际上就是在容量下界全部满足的前提下尽量多得获得“自由流量”。 22 注意,在这张已经存在流量的图上,弧<t,s>也是存在流量的,千万不要忽略这条弧。 23 因为它的相反弧<s,t>的流量为<t,s>的流量的相反数,且<s,t>的容量为0,所以这部分的流量也是会被算上的。 24 */ 25 #include <bits/stdc++.h> 26 using namespace std; 27 typedef long long ll; 28 const int LEN = 1e5 + 5; 29 const int oo = (1LL << 31) - 1; 30 namespace DINIC { 31 int tot, n, m, src, tar, qh, qt, cnt, s, t, S, T; 32 int ans, flow; 33 struct edge { 34 int vet, next, len; 35 } E[LEN * 2]; 36 int dis[LEN], gap[LEN], head[LEN], cur[LEN], q[LEN], vis[LEN], IN[LEN]; 37 void add(int u, int v, int c) { 38 E[++tot] = (edge){v, head[u], c}; 39 head[u] = tot; 40 } 41 void join(int u, int v, int c) { 42 add(u, v, c); 43 add(v, u, 0); 44 } 45 void init() { 46 tot = -1; 47 for (int i = 1; i <= cnt; i++) head[i] = -1; 48 } 49 bool bfs() { 50 for (int i = 1; i <= cnt; i++) dis[i] = 0; 51 qh = qt = 0; 52 q[++qt] = src; 53 dis[src] = 1; 54 while (qh < qt) { 55 int u = q[++qh]; 56 for (int e = head[u]; e != -1; e = E[e].next) { 57 int v = E[e].vet; 58 if (E[e].len && !dis[v]) { 59 dis[v] = dis[u] + 1; 60 if (v == tar) return 1; 61 q[++qt] = v; 62 } 63 } 64 } 65 return dis[tar]; 66 } 67 int dfs(int u, int aug) { 68 if (u == tar || !aug) return aug; 69 int tmp = 0; 70 for (int &e = cur[u]; e != -1; e = E[e].next) { 71 int v = E[e].vet; 72 if (dis[v] == dis[u] + 1) { 73 if (tmp = dfs(v, min(aug, E[e].len))) { 74 E[e].len -= tmp; 75 E[e ^ 1].len += tmp; 76 return tmp; 77 } 78 } 79 } 80 return 0; 81 } 82 int maxflow(int s, int t) { 83 src = s, tar = t; 84 int res = 0, flow = 0; 85 while (bfs()) { 86 for (int i = 1; i <= cnt; i++) cur[i] = head[i]; 87 while (flow = dfs(src, oo)) res += flow; 88 } 89 return res; 90 } 91 } 92 using namespace DINIC; 93 int main() { 94 scanf("%d %d %d %d", &n, &m, &s, &t); 95 cnt = n; 96 S = ++cnt, T = ++cnt; 97 init(); 98 for (int i = 1; i <= m; i++) { 99 int x, y, l, r; 100 scanf("%d %d %d %d", &x, &y, &l, &r); 101 join(x, y, r - l); 102 IN[y] += l, IN[x] -= l; 103 } 104 for (int i = 1; i <= n; i++) { 105 if (IN[i] < 0) join(i, T, -IN[i]); 106 else if (IN[i] > 0) { 107 flow += IN[i]; 108 join(S, i, IN[i]); 109 } 110 } 111 join(t, s, oo); 112 ans = maxflow(S, T); 113 if (ans != flow) puts("please go home to sleep"); 114 else { 115 ans = maxflow(s, t); 116 printf("%lld ", ans); 117 } 118 return 0; 119 }
1 /* 2 先来看有源汇可行流 3 建模方法: 4 建立弧<t,s>,容量下界为0,上界为oo。 5 然后对这个新图(实际上只是比原图多了一条边)按照无源汇可行流的方法建模, 6 如果所有附加弧满流,则存在可行流。 7 求原图中每条边对应的实际流量的方法,同无源汇可行流,只是忽略掉弧<t,s>就好。 8 而且这时候弧<t,s>的流量就是原图的总流量。 9 理解方法: 10 有源汇相比无源汇的不同就在于,源和汇是不满足流量平衡的,那么连接<t,s>之后, 11 源和汇也满足了流量平衡,就可以直接按照无源汇的方式建模。 12 注意:这张图的最大流只是对应着原图的一组可行流,而不是原图的最大或最小流。 13 14 有源汇最小流 15 有源汇最小流的常见建模方法比较多,我就只说我常用的一种。 16 建模方法: 17 首先按照有源汇可行流的方法建模,但是不要建立<t,s>这条弧。 18 然后在这个图上,跑从附加源ss到附加汇tt的最大流。 19 这时候再添加弧<t,s>,下界为0,上界oo。 20 在现在的这张图上,从ss到tt的最大流,就是原图的最小流。 21 理解方法: 22 我们前面提到过,有源汇可行流的流量只是对应一组可行流,并不是最大或者最小流。 23 并且在跑完有源汇可行流之后,弧<t,s>的流量就是原图的流量。 24 从这个角度入手,我们想让弧<t,s>的流量尽量小,就要尽量多的消耗掉那些“本来不需要经过<t,s>”的流量。 25 于是我们在添加<t,s>之前,跑一遍从ss到tt的最大流,就能尽量多的消耗那些流量啦QwQ。 26 */ 27 #include <bits/stdc++.h> 28 using namespace std; 29 typedef long long ll; 30 const int LEN = 2e5 + 5; 31 const int oo = (1LL << 31) - 1; 32 namespace DINIC { 33 int tot, n, m, src, tar, qh, qt, cnt, s, t, S, T, ans, flow; 34 struct edge { 35 int vet, next, len; 36 } E[LEN * 2]; 37 int dis[LEN], gap[LEN], head[LEN], cur[LEN], q[LEN], vis[LEN], IN[LEN]; 38 void add(int u, int v, int c) { 39 E[++tot] = (edge){v, head[u], c}; 40 head[u] = tot; 41 } 42 void join(int u, int v, int c) { 43 add(u, v, c); 44 add(v, u, 0); 45 } 46 void init() { 47 tot = -1; 48 for (int i = 1; i <= cnt; i++) head[i] = -1; 49 } 50 bool bfs() { 51 for (int i = 1; i <= cnt; i++) dis[i] = 0; 52 qh = qt = 0; 53 q[++qt] = src; 54 dis[src] = 1; 55 while (qh < qt) { 56 int u = q[++qh]; 57 for (int e = head[u]; e != -1; e = E[e].next) { 58 int v = E[e].vet; 59 if (E[e].len && !dis[v]) { 60 dis[v] = dis[u] + 1; 61 if (v == tar) return 1; 62 q[++qt] = v; 63 } 64 } 65 } 66 return dis[tar]; 67 } 68 int dfs(int u, int aug) { 69 if (u == tar || !aug) return aug; 70 int tmp = 0; 71 for (int &e = cur[u]; e != -1; e = E[e].next) { 72 int v = E[e].vet; 73 if (dis[v] == dis[u] + 1) { 74 if (tmp = dfs(v, min(aug, E[e].len))) { 75 E[e].len -= tmp; 76 E[e ^ 1].len += tmp; 77 return tmp; 78 } 79 } 80 } 81 return 0; 82 } 83 int maxflow(int s, int t) { 84 src = s, tar = t; 85 int res = 0, flow = 0; 86 while (bfs()) { 87 for (int i = 1; i <= cnt; i++) cur[i] = head[i]; 88 while (flow = dfs(src, oo)) res += flow; 89 } 90 return res; 91 } 92 } 93 using namespace DINIC; 94 int main() { 95 scanf("%d %d %d %d", &n, &m, &s, &t); 96 cnt = n; 97 S = ++cnt, T = ++cnt; 98 init(); 99 for (int i = 1; i <= m; i++) { 100 int x, y, l, r; 101 scanf("%d %d %d %d", &x, &y, &l, &r); 102 join(x, y, r - l); 103 IN[y] += l, IN[x] -= l; 104 } 105 for (int i = 1; i <= n; i++) { 106 if (IN[i] < 0) join(i, T, -IN[i]); 107 else if (IN[i] > 0) { 108 flow += IN[i]; 109 join(S, i, IN[i]); 110 } 111 } 112 ans = maxflow(S, T); 113 flow -= ans; 114 join(t, s, oo); 115 ans = maxflow(S, T); 116 if (ans != flow) puts("please go home to sleep"); 117 else printf("%d ", ans); 118 return 0; 119 }