最大流总结
Posted 我不吃饼干呀
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最大流总结相关的知识,希望对你有一定的参考价值。
准备开始学习最大流。模板是在网上抄的,感觉这个看起来比较优雅。http://blog.csdn.net/d891320478/article/details/8424820
持续更新。
(hdu zoj poj vj 都挂了 还怎么刷题啊……)
(2016.9.6
题意:一个小组有n个队伍1~n,每场比赛一个队伍赢,一个队伍输,现在已知每个队伍i都已经赢了Wi场比赛,同时知道每个队伍还需要打Ri场比赛,包括组内和组外,同时知道组内还剩下的比赛场数,Aij表示i和j还需要打几场比赛,Aij=Aji,∑(j:1~n)Aij <= Ri
问题是队伍1能否成为小组获胜场数最多的,可以并列第一。
>>首先让队伍1赢得所有的比赛,其他队伍输掉所有组外的比赛。然后建图最大流。
/* * 建一个源点 然后对于每一场比赛为一个点 从源点到比赛连边,权值为比赛的总分数 * 比赛这个点和比赛的两个队伍分别连边 权值大于等于比赛最大分数即可 * 每个队伍到汇点连一条边 权值应为该队伍和1队分数的差值(如果分数大于1队,应该直接输出no * 走一遍最大流 如果答案是所有比赛的分数和就可以满足要求 */ #include <bits/stdc++.h> const int N = 1000; const int M = 1000000; const int INF = 1 << 30; struct Edge { int to, next, w; } edge[M]; int head[N], cntE; int src, sink; int pre[N], cur[N], dis[N], gap[N]; int que[N], open, tail; void addedge(int u, int v, int w) { edge[cntE].to = v; edge[cntE].w = w; edge[cntE].next = head[u]; head[u] = cntE++; edge[cntE].to = u; edge[cntE].w = 0; edge[cntE].next = head[v]; head[v] = cntE++; } void BFS() { int i, u, v; memset(gap, 0, sizeof(gap)); memset(dis, -1, sizeof(dis)); open = tail = 0; que[open] = sink; dis[sink] = 0; while (open <= tail) { u = que[open++]; for (i = head[u]; i != -1; i = edge[i].next) { v = edge[i].to; if (edge[i].w != 0 || dis[v] != -1) continue; que[++tail] = v; ++gap[dis[v] = dis[u] + 1]; } } } int sap(int n) { //编号从1开始 1~n int i, v, u, flow = 0, aug = INF; int flag; BFS(); gap[0] = 1; for (i = 1; i <= n; i++) cur[i] = head[i]; u = pre[src] = src; while (dis[src] < n) { flag = 0; for (int j = cur[u]; j != -1; j = edge[j].next) { v = edge[j].to; if (edge[j].w > 0 && dis[u] == dis[v] + 1) { flag = 1; if (edge[j].w < aug) aug = edge[j].w; pre[v] = u; u = v; if (u == sink) { flow += aug; while (u != src) { u = pre[u]; edge[cur[u]].w -= aug; edge[cur[u] ^ 1].w += aug; } aug = INF; } break; } cur[u] = edge[j].next; } if (flag) continue; int mindis = n; for (int j = head[u]; j != -1; j = edge[j].next) { v = edge[j].to; if (edge[j].w > 0 && mindis > dis[v]) { mindis = dis[v]; cur[u] = j; } } if (--gap[dis[u]] == 0) break; ++gap[dis[u] = mindis + 1]; u = pre[u]; } return flow; } int w[N], r[N], a[N][N]; int main() { int n; while (~scanf("%d", &n)) { for (int i = 1; i <= n; ++i) scanf("%d", w+i);//每个队伍已经赢了多少场比赛 for (int i = 1; i <= n; ++i) scanf("%d", r+i);//每个队伍还可以打多少场比赛,包括本组和外组 for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j) scanf("%d", &a[i][j]);//本组剩余比赛情况 w[1] += r[1]; //贪心 让本队全赢 bool fg = false; for (int i = 2; i <= n; ++i) if (w[i] > w[1]) { fg = true; break; } if (fg) { puts("NO"); continue; } src = 1; int cnt = n+1; int sum = 0; cntE = 0; memset(head, -1, sizeof head); for (int i = 2; i <= n; ++i) for (int j = i; j <= n; ++j) { if (a[i][j] <= 0) continue; sum += a[i][j]; addedge(src, cnt, a[i][j]); addedge(cnt, i, INF); addedge(cnt, j, INF); cnt++; } sink = cnt; for (int i = 2; i <= n; ++i) addedge(i, sink, w[1]-w[i]); if (sap(sink) == sum) puts("YES"); else puts("NO"); } return 0; }
(2016.9.7
codeforces546E-Soldier and Traveling
题意:有n个城市,m条无向图,每个城市有ai的士兵,每个士兵可以走到相邻的城市(只能走一步)或者原地不动。问能不能使得每个城市的士兵变成bi。
题解:首先有一个trick,就是sigma(ai)!=sigma(bi)的时候,记得输出NO。
>>这题需要输出点与点的转移数量,在求最大流的过程中记录
/* 把每个点拆成入点和出点 * 从源点到每一个入点连一条 流量为该点的初始人数 * 从每个点的入点到出点连一条边 流量为该点的初始人数 * 对于每条边 从起点的入点到终点的出点连一条边 流量为起点的初始人数 * 每个出点连汇点 流量为该点的结束人数 */ #include <bits/stdc++.h> const int N = 1000; const int M = 1000000; const int INF = 1 << 30; struct Edge { int from, to, next, w; } edge[M]; int head[N], cntE; int src, sink; int pre[N], cur[N], dis[N], gap[N]; int que[N], open, tail; int ans[N][N]; void addedge(int u, int v, int w) { edge[cntE].from = u; edge[cntE].to = v; edge[cntE].w = w; edge[cntE].next = head[u]; head[u] = cntE++; edge[cntE].from = v; edge[cntE].to = u; edge[cntE].w = 0; edge[cntE].next = head[v]; head[v] = cntE++; } void BFS() { int i, u, v; memset(gap, 0, sizeof(gap)); memset(dis, -1, sizeof(dis)); open = tail = 0; que[open] = sink; dis[sink] = 0; while (open <= tail) { u = que[open++]; for (i = head[u]; i != -1; i = edge[i].next) { v = edge[i].to; if (edge[i].w != 0 || dis[v] != -1) continue; que[++tail] = v; ++gap[dis[v] = dis[u] + 1]; } } } int sap(int n) { //编号从1开始 1~n int i, v, u, flow = 0, aug = INF; int flag; BFS(); gap[0] = 1; for (i = 1; i <= n; i++) cur[i] = head[i]; u = pre[src] = src; while (dis[src] < n) { flag = 0; for (int j = cur[u]; j != -1; j = edge[j].next) { v = edge[j].to; if (edge[j].w > 0 && dis[u] == dis[v] + 1) { flag = 1; if (edge[j].w < aug) aug = edge[j].w; pre[v] = u; u = v; if (u == sink) { flow += aug; while (u != src) { u = pre[u]; edge[cur[u]].w -= aug; ans[edge[cur[u]].from][edge[cur[u]].to] += aug; edge[cur[u] ^ 1].w += aug; ans[edge[cur[u] ^ 1].from][edge[cur[u] ^ 1].to] -= aug; } aug = INF; } break; } cur[u] = edge[j].next; } if (flag) continue; int mindis = n; for (int j = head[u]; j != -1; j = edge[j].next) { v = edge[j].to; if (edge[j].w > 0 && mindis > dis[v]) { mindis = dis[v]; cur[u] = j; } } if (--gap[dis[u]] == 0) break; ++gap[dis[u] = mindis + 1]; u = pre[u]; } return flow; } int a[N], b[N]; int main() { int n, m, p, q; while (~scanf("%d%d", &n, &m)) { cntE = 0; memset(head, -1, sizeof head); memset(ans, 0, sizeof ans); src = 2 * n + 1, sink = 2 * n + 2; int sum = 0; for (int i = 1; i <= n; ++i) { scanf("%d", a+i); sum += a[i]; addedge(src, i, a[i]); addedge(i, i+n, a[i]); } int sum2 = 0; for (int i = 1; i <= n; ++i) { scanf("%d", b+i); sum2 += b[i]; addedge(i+n, sink, b[i]); } for (int i = 1; i <= m; ++i) { scanf("%d%d", &p, &q); addedge(p, q+n, a[p]); addedge(q, p+n, a[q]); } if (sum != sum2) { puts("NO"); continue; } if (sap(sink) != sum) { puts("NO"); } else { puts("YES"); for (int i = 1; i <= n; ++i) { for (int j = 1; j <= n; ++j) { printf("%d%c", ans[i][j+n], j==n?\'\\n\':\' \'); } } } } return 0; }
题意:有n个点m条有向边,每个边有一个容量,从这个边走过的总重量不能超过这个容量,无重边和自环。现在有x只小熊,需要所有的熊从1出发走到n点,每只熊携带相同质量的货物,问最多能运多少货物。
>>开始还以为容量需要小数,后来发现想多了。。。精度什么的,1e-8wa,1e-10超时,改成循环100遍竟然就对了。。。。为什么这么神奇呢。。。。
/* 首先二分每只熊所带的货物重量 * 对于每条路径,用路径容量除以每只熊的重量就是这条路所能通过熊的数量 * 因为熊携带的重量会很小,容量又很大,所以可能爆int。。注意下。。 * 要从源点到1建一个容量为x的边 * 当最大流为x的时候 说明所有熊都可以走到终点 */ #include <bits/stdc++.h> const int N = 1000; const int M = 1000000; const int INF = 1 << 30; struct Edge { int to, next, w; } edge[M]; int head[N], cntE; int src, sink; int pre[N], cur[N], dis[N], gap[N]; int que[N], open, tail; void addedge(int u, int v, int w) { edge[cntE].to = v; edge[cntE].w = w; edge[cntE].next = head[u]; head[u] = cntE++; edge[cntE].to = u; edge[cntE].w = 0; edge[cntE].next = head[v]; head[v] = cntE++; } void BFS() { int i, u, v; memset(gap, 0, sizeof(gap)); memset(dis, -1, sizeof(dis)); open = tail = 0; que[open] = sink; dis[sink] = 0; while (open <= tail) { u = que[open++]; for (i = head[u]; i != -1; i = edge[i].next) { v = edge[i].to; if (edge[i].w != 0 || dis[v] != -1) continue; que[++tail] = v; ++gap[dis[v] = dis[u] + 1]; } } } int sap(int n) { //编号从1开始 1~n int i, v, u, flow = 0, aug = INF; int flag; BFS(); gap[0] = 1; for (i = 1; i <= n; i++) cur[i] = head[i]; u = pre[src] = src; while (dis[src] < n) { flag = 0; for (int j = cur[u]; j != -1; j = edge[j].next) { v = edge[j].to; if (edge[j].w > 0 && dis[u] == dis[v] + 1) { flag = 1; if (edge[j].w < aug) aug = edge[j].w; pre[v] = u; u = v; if (u == sink) { flow += aug; while (u != src) { u = pre[u]; edge[cur[u]].w -= aug; edge[cur[u] ^ 1].w += aug; } aug = INF; } break; } cur[u] = edge[j].next; } if (flag) continue; int mindis = n; for (int j = head[u]; j != -1; j = edge[j].next) { v = edge[j].to; if (edge[j].w > 0 && mindis > dis[v]) { mindis = dis[v]; cur[u] = j; } } if (--gap[dis[u]] == 0) break; ++gap[dis[u] = mindis + 1]; u = pre[u]; } return flow; } int a[N], b[N], c[N]; int main() { int n, m, x; scanf("%d%d%d", &n, &m, &x); double l = 0, r = 0; for (int i = 0; i < m; ++i) { scanf("%d%d%d", a+i, b+i, c+i); r += c[i]; } src = n+1, sink = n; int cnt= 100; while (cnt--) { double mid = (l+r) / 2; memset(head, -1, sizeof head); cntE = 0; for (int i = 0; i < m; ++i) { int cap = std::min(floor(c[i]/mid), 1e8); addedge(a[i], b[i], cap); } addedge(src, 1, x); int tmp = sap(n+1); if (tmp == x) l = mid; else r = mid; } printf("%.10f\\n", l*x); }
题意:有n个狐狸1~n,每个狐狸ai岁。现在需要将狐狸围桌,一个桌子的狐狸数目要大于等于3,同时相邻的两只狐狸年龄加起来需要是一个质数。问分桌方式,不能分就输出不可能。
>>开始错的很离谱,发现判断质数写错了。。。后来又错的很奇怪,发现sap写错了。。。。
感觉这题比较难,根据奇偶构图第一次见到,学习了。。
/* 首先容易对于两只狐狸,如果年龄和为质数,连边 * 因为质数都是奇数(ai>=2)可知相邻的狐狸年龄一定是一奇一偶 * 那么一个桌子的奇数狐狸=偶数狐狸>=2 * 从源点到每一个偶数建一个流量为2的边 * 每一个奇数到汇点建一个流量为2的边 * 然后年龄和为质数的两只狐狸之间建一条流量为1的边 * 然后跑最大流 如果流量为n就可以 * 输出比较麻烦。。看完建图题解。。这里还想了半天Orz */ #include <bits/stdc++.h> const int N = 1000; const int M = 1000000; const int INF = 1 << 30; struct Edge { int from, to, next, w; } edge[M]; int head[N], cntE; int src, sink; int pre[N], cur[N], dis[N], gap[N]; int que[N], open, tail; int ans[N][N]; void addedge(int u, int v, int w) { edge[cntE].from = u; edge[cntE].to = v; edge[cntE].w = w; edge[cntE].next = head[u]; head[u] = cntE++; edge[cntE].from = v; edge[cntE].to = u; edge[cntE].w = 0; edge[cntE].next = head[v]; head[v] = cntE++; } void BFS() { int i, u, v; memset(gap, 0, sizeof(gap)); memset(dis, -1, sizeof(dis)); open = tail = 0; que[open] = sink; dis[sink] = 0; while (open <= tail) { u = que[open++]; for (i = head[u]; ~i; i = edge[i].next) { v = edge[i].to; if (edge[i].w != 0 || dis[v] != -1) continue; que[++tail] = v; ++gap[dis[v] = dis[u] + 1]; } } } int sap(int n) { //编号从1开始 1~n int i, v, u, flow = 0, aug = INF; int flag; BFS(); gap[0] = 1; for (i = 1; i <= n; i++) cur[i] = head[i]; u = pre[src] = src; while (dis[src] < n) { flag = 0; for (int j = cur[u]; ~j; j = edge[j].next) { v = edge[j].to; if (edge[j].w > 0 && dis[u] == dis[v] + 1) { flag = 1; if (edge[j].w < aug) aug = edge[j].w; pre[v] = u; u = v; if (u == sink) { flow += aug; while (u != src) { u = pre[u]; edge[cur[u]].w -= aug; edge[cur[u] ^ 1].w += aug; ans[edge[cur[u]].from][edge[cur[u]].to] += aug; ans[edge[cur[u]^1].from][edge[cur[u]^1].to] -= aug; } aug = INF; } break; } cur[u] = edge[j].next; } if (flag) continue; int mindis = n; for (int j = head[u]; ~j; j = edge[j].next) { v = edge[j].to; if (edge[j].w > 0 && mindis > dis[v]) { mindis = dis[v]; cur[u] = j; } } if (--gap[dis[u]] == 0) break; ++gap[dis[u] = mindis + 1]; u = pre[u]; } return flow; } bool isprime(int x) { int limit = sqrt(x); for (int i = 2; i <= limit; ++i) if (x % i == 0) return false; return true; } int a[N]; int vis[N]; std::vector<int> G[N]; std::vector<int> ret[N]; void dfs(int x, int u) { vis[u] = 1; ret[x].push_back(u); for (unsigned i = 0; i < G[u].size(); ++i) { int v = G[u][i]; if (!vis[v]) { dfs(x, v); break;} } } int main() { memset(head, -1, sizeof head); cntE = 0; int n; scanf("%d", &n); for (int i = 1; i <= n; ++i) { scanf("%d", a+i); } src = n+1, sink = n+2; int odd = 0, even = 0;; for (int i = 1; i <= n; ++i) if (a[i] & 1) addedge(i, sink, 2), ++odd; else addedge(src, i, 2), ++even; if (odd != even) { puts("Impossible"); return 0; } for (int i = 1; i <= n; ++i) for (int j = i+1; j <= n; ++j) if (isprime(a[i]+a[j])) { if (a[i] & 1) addedge(j, i, 1); else addedge(i, j, 1); } if (sap(sink) != n) { puts("Impossible"); } else { for (int i = 1; i <= n; ++i) { 网络流24题-题目总结