网络流学习笔记2
来源:互联网 发布:js数组和对象 编辑:程序博客网 时间:2024/06/05 20:14
强盗
听过讲解后1A…
在一定时间周期内有若干个强盗,可以在
由于可以钦定,那必然是让他们抢劫在尽可能多的不同时间内。于是想到二分图带权匹配。由于边数太多,而且时间总是以区间形式出现,不妨在时间上建一棵线段树,然后连边跑费用流。
#include <bits/stdc++.h>using namespace std;const int MAXN = 40005, MAXM = 5000005;struct node { int to, next, f, c, neg;} edge[MAXM];int head[MAXN], top = 0;void push(int i, int j, int k, int l){ //printf("%d--%d,%d-->%d\n", i, k, l, j); ++top, edge[top] = (node) {j, head[i], k, l, top+1}, head[i] = top; ++top, edge[top] = (node) {i, head[j], 0, -l, top-1},head[j] = top;}int vis[MAXN], dis[MAXN], S = 40001, T = 40002, n, m = 1<<13;int pre[MAXN], pre_edge[MAXN];const int inf = 23333333;queue<int> que;bool spfa(){ memset(vis, 0, sizeof vis); memset(pre, 0, sizeof pre); memset(dis, 127/3, sizeof dis); vis[S] = 1, dis[S] = 0, que.push(S); while (!que.empty()) { int tp = que.front(); que.pop(); vis[tp] = 0; for (int i = head[tp]; i; i = edge[i].next) { if (!edge[i].f || dis[edge[i].to] <= dis[tp] + edge[i].c) continue; dis[edge[i].to] = dis[tp] + edge[i].c; pre[edge[i].to] = tp, pre_edge[edge[i].to] = i; if (!vis[edge[i].to]) vis[edge[i].to] = 1, que.push(edge[i].to); } } return dis[T] <= inf;}int work(int &cost){ int ans = inf; for (int i = T; i != S; i = pre[i]) ans = min(ans, edge[pre_edge[i]].f); for (int i = T; i != S; i = pre[i]) edge[pre_edge[i]].f -= ans, edge[edge[pre_edge[i]].neg].f += ans; cost += ans*dis[T]; return ans;}int mcf(int &cost){ int ans = 0; while (spfa()) ans += work(cost); return ans;}int main(){ for (int i = m; i <= m+m-1; i++) push(i, T, 1, 0); for (int i = m-1; i >= 1; i--) push(i, i*2, inf, 0), push(i, i*2+1, inf, 0); scanf("%d", &n); for (int i = 1; i <= n; i++){ int a, b, c; scanf("%d%d%d", &a, &b, &c); b--; for (a += m-1, b += m-1; a < b; a >>= 1, b >>= 1) { if (a&1) push(m+m+i, a++, inf, 0); if (!(b&1)) push(m+m+i, b--, inf, 0); } if (a == b) push(m+m+i, a, inf, 0); push(S, m+m+i, 1, -c); } int cost = 0; mcf(cost); cout << -cost << endl; return 0;}
NOI2008 志愿者招募
貌似是simplex的裸题…但是simplex调了一会调不出来,于是只好看题解写费用流。
建图方法比(ji)较(qi)诡异…
按时刻拆点,每个时刻连向下一个时刻为流量下界为
#include <bits/stdc++.h>using namespace std;const int MAXN = 1005, MAXM = 200000;struct node { int to, next, f, c, neg;} edge[MAXM];int head[MAXN], top = 0;void push(int i, int j, int k, int l){ //printf("%d--%d,%d-->%d\n", i, k, l, j); ++top, edge[top] = (node) {j, head[i], k, l, top+1}, head[i] = top; ++top, edge[top] = (node) {i, head[j], 0, -l, top-1}, head[j] = top;}int n, m;int S = 1002, T = 1003;const int inf = 23333333;void push_lim(int i, int j, int low){ push(S, j, low, 0); push(i, T, low, 0); push(i, j, inf, 0);}int vis[MAXN], dis[MAXN];int pre[MAXN], pre_edge[MAXN];queue<int> que;bool spfa(){ memset(vis, 0, sizeof vis); memset(dis, 127/3, sizeof dis); memset(pre, 0, sizeof pre); vis[S] = 1, dis[S] = 0, que.push(S); while (!que.empty()) { int tp = que.front(); que.pop(); vis[tp] = 0; for (int i = head[tp]; i; i = edge[i].next) { if (edge[i].f == 0 || dis[edge[i].to] <= dis[tp] + edge[i].c) continue; dis[edge[i].to] = dis[tp] + edge[i].c; pre[edge[i].to] = tp; pre_edge[edge[i].to] = i; if (!vis[edge[i].to]) vis[edge[i].to] = 1, que.push(edge[i].to); } } return dis[T] <= inf;}int work(int &cost){ int ans = inf; for (int i = T; i != S; i = pre[i]) ans = min(ans, edge[pre_edge[i]].f); for (int i = T; i != S; i = pre[i]) edge[pre_edge[i]].f -= ans, edge[edge[pre_edge[i]].neg].f += ans; cost += dis[T] * ans; //cout << dis[T] << "--" << endl; //cin.get(); return ans;}int mcf(int &cost){ int ans = 0; while (spfa()) ans += work(cost); return ans;}int main(){ scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) { int u; scanf("%d", &u); push_lim(i, i+1, u); } for (int i = 1; i <= m; i++) { int s, t, c; scanf("%d%d%d", &s, &t, &c); push(t+1, s, inf, c); } int cost = 0; mcf(cost); cout << cost << endl; return 0;}
luogu1231 教辅的组成
经典的拆点建图。
#include <bits/stdc++.h>using namespace std;const int MAXN = 100005, MAXM = 5000005;struct node { int to, next, f, neg;} edge[MAXM];int head[MAXN], top = 0;void push(int i, int j, int k){ //printf("%d--%d-->%d\n", i, k, j); ++top, edge[top] = (node) {j, head[i], k, top+1} , head[i] = top; ++top, edge[top] = (node) {i, head[j], 0, top-1} , head[j] = top;}int vis[MAXN], bfstime = 0;int lev[MAXN], que[MAXM], l = 0, r = 0;int n1,n2,n3, m1,m2, S = 50001, T = 50002;const int inf = 23333333;bool bfs(){ vis[S] = ++bfstime, lev[S] = 0, que[++r] = S; while (l != r) { int tp = que[++l]; for (int i = head[tp]; i; i = edge[i].next) { if (vis[edge[i].to] == bfstime || edge[i].f == 0) continue; lev[edge[i].to] = lev[tp] + 1, vis[edge[i].to] = bfstime, que[++r] = edge[i].to; } } return vis[T] == bfstime;}int dfs(int nd, int maxf = inf){ if (nd == T || maxf == 0) return maxf; int ans = 0, t; for (int i = head[nd]; i; i = edge[i].next) { if (edge[i].f == 0 || lev[edge[i].to] != lev[nd]+1) continue; t = dfs(edge[i].to, min(maxf, edge[i].f)); ans += t, maxf -= t; edge[i].f -= t, edge[edge[i].neg].f += t; } return ans;}int dinic(){ int ans = 0; while (bfs()) ans += dfs(S); return ans;}int main(){ scanf("%d%d%d", &n1, &n2, &n3); scanf("%d", &m1); for (int i = 1; i <= n1; i++) push(10000+i, 20000+i, 1); for (int i = 1; i <= n2; i++) push(S, i, 1); for (int i = 1; i <= n3; i++) push(30000+i, T, 1); for (int i = 1; i <= m1; i++) { int u, v; scanf("%d%d", &u,&v); push(v, 10000+u, 1); } scanf("%d", &m2); for (int i = 1; i <= m2; i++) { int u, v; scanf("%d%d", &u, &v); push(u+20000, 30000+v, 1); } cout << dinic() << endl; return 0;}
ZJOI2009 假期的宿舍
1A..
裸的二分图匹配,最大流就过了..
#include <bits/stdc++.h>using namespace std;const int MAXN = 305, MAXM = 100005;struct node { int to, next, f, neg;} edge[MAXM];int head[MAXN], top = 0;void push(int i, int j, int k){ //printf("%d---%d-->%d\n", i, k, j); ++top, edge[top] = (node){ j, head[i], k, top+1}, head[i] = top; ++top, edge[top] = (node){ i, head[j], 0, top-1}, head[j] = top;}int lev[MAXN], vis[MAXN], bfstime = 0;queue<int> que;int S = 301, T = 302, n;const int inf = 23333333;bool bfs(){ vis[S] = ++bfstime, lev[S] = 0, que.push(S); while (!que.empty()) { int tp = que.front(); que.pop(); //cout << tp << endl; for (int i = head[tp]; i; i = edge[i].next) { if (edge[i].f == 0 || vis[edge[i].to] == bfstime) continue; vis[edge[i].to] = bfstime, lev[edge[i].to] = lev[tp]+1, que.push(edge[i].to); } } //cout << "V" << vis[T] << endl; return vis[T] == bfstime;}int dfs(int nd, int maxf = inf){ if (nd == T || !maxf) return maxf; int ans = 0, t; for (int i = head[nd]; i; i = edge[i].next ) { if (edge[i].f == 0 || lev[edge[i].to] != lev[nd]+1) continue; t = dfs(edge[i].to, min(maxf, edge[i].f)); ans += t, maxf -= t; edge[i].f -= t, edge[edge[i].neg].f += t; } if (maxf) lev[nd] = -1; return ans;}int dinic(){ int ans = 0; while (bfs()) ans += dfs(S); return ans;}inline int number(int i){ return (i-1)* 2+1; }int stay[MAXN];int main(){ int t; scanf("%d", &t); while (t--) { scanf("%d", &n); top = 0; memset(head, 0, sizeof head); for (int i = 1; i <= n; i++) { scanf("%d", &stay[i]); if (stay[i] == 1) push(number(i)+1, T, 1); } int cnt = 0; for (int i = 1; i <= n; i++) { int u; scanf("%d", &u); if (stay[i] == 0 || (stay[i] == 1 && u == 0)) push(S, number(i), 1), cnt++; push(number(i), number(i)+1, 1); } for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) { int u; scanf("%d", &u); if (u) push(number(i), number(j)+1, 1); } //cout << dinic() << endl; if (dinic() == cnt) puts("^_^"); else puts("T_T"); } return 0;}
SDOI2009 晨跑
Elaxia最近迷恋上了空手道,他为自己设定了一套健身计划,比如俯卧撑、仰卧起坐等 等,不过到目前为止,他坚持下来的只有晨跑。 现在给出一张学校附近的地图,这张地图中包含N个十字路口和M条街道,Elaxia只能从 一个十字路口跑向另外一个十字路口,街道之间只在十字路口处相交。Elaxia每天从寝室出发 跑到学校,保证寝室编号为1,学校编号为N。 Elaxia的晨跑计划是按周期(包含若干天)进行的,由于他不喜欢走重复的路线,所以 在一个周期内,每天的晨跑路线都不会相交(在十字路口处),寝室和学校不算十字路 口。Elaxia耐力不太好,他希望在一个周期内跑的路程尽量短,但是又希望训练周期包含的天 数尽量长。 除了练空手道,Elaxia其他时间都花在了学习和找MM上面,所有他想请你帮忙为他设计 一套满足他要求的晨跑计划。
不会相交就是点拆边中间流量为1费用为0,最小费用就是边上流量为1费用为长度,然后跑最小费用最大流。
大坑:1->n
#include <bits/stdc++.h>using namespace std;const int MAXN = 805, MAXM = 100005;struct node { int to, next, f, c, neg;} edge[MAXM];int head[MAXN], top = 0;void push(int i, int j, int k, int l){ ++top, edge[top] = (node){j, head[i], k, l, top+1}, head[i] = top; ++top, edge[top] = (node){i, head[j], 0, -l, top-1}, head[j] = top;}int inf = 233333333;int n, m, S = 801, T = 802;int vis[MAXN], dis[MAXN], pre[MAXN], pre_edge[MAXN];queue<int> que;bool spfa(){ memset(vis, 0, sizeof vis); memset(dis, 127/3, sizeof dis); vis[S] = 1, dis[S] = 0, que.push(S); while (!que.empty()) { int tp = que.front(); que.pop(), vis[tp] = 0; //cout << tp << endl; for (int i = head[tp]; i; i = edge[i].next) { if (!edge[i].f || dis[edge[i].to] <= dis[tp]+edge[i].c) continue; dis[edge[i].to] = dis[tp]+edge[i].c; pre[edge[i].to] = tp, pre_edge[edge[i].to] = i; if (!vis[edge[i].to]) vis[edge[i].to] = 1, que.push(edge[i].to); } } return dis[T] <= inf;}int work(int &cost){ int ans = inf; for (int i = T; i != S; i = pre[i]) ans = min(ans, edge[pre_edge[i]].f); for (int i = T; i != S; i = pre[i]) edge[pre_edge[i]].f -= ans, edge[edge[pre_edge[i]].neg].f += ans; cost += dis[T]*ans; return ans;}int mcf(int &cost){ int ans = 0; while (spfa()) ans += work(cost); return ans;}int main(){ scanf("%d%d", &n, &m); push(1, n+1, inf, 0); push(n, n+n, inf, 0); push(S, 1, inf, 0); push(n+n, T, inf, 0); for (int i = 2; i <= n-1; i++) push(i, n+i, 1, 0); for (int i = 1; i <= m; i++) { int a, b, c; scanf("%d%d%d", &a, &b, &c); if (a == 1 && b == n || a == n && b == 1) { push(1+n, n, 1, c); } else { push(a+n, b, inf, c); } } int ans = 0, cost = 0; ans = mcf(cost); printf("%d %d\n", ans, cost); return 0;}
ZJOI2010 网络扩容
1A…
扩容就是在残量网络上加有费用的无限流量边,为了让他只增加k,在源点和汇点外面接超级源点和超级汇点,流量都为k。然后跑最小费用最大流。
#include <bits/stdc++.h>using namespace std;const int MAXN = 1005, MAXM = 100005;struct node { int to, next, f, c, neg;} edge[MAXM];int head[MAXN], top = 0;void push(int i, int j, int k, int l){ ++top, edge[top] = (node){j, head[i], k, l, top+1}, head[i] = top; ++top, edge[top] = (node){i, head[j], 0, -l, top-1}, head[j] = top;}int inf = 233333333;int n, m, k, S = 1001, T = 1002;int vis[MAXN], dis[MAXN], pre[MAXN], pre_edge[MAXN];queue<int> que;bool spfa(){ memset(vis, 0, sizeof vis); memset(dis, 127/3, sizeof dis); vis[S] = 1, dis[S] = 0, que.push(S); while (!que.empty()) { int tp = que.front(); que.pop(), vis[tp] = 0; //cout << tp << endl; for (int i = head[tp]; i; i = edge[i].next) { if (!edge[i].f || dis[edge[i].to] <= dis[tp]+edge[i].c) continue; dis[edge[i].to] = dis[tp]+edge[i].c; pre[edge[i].to] = tp, pre_edge[edge[i].to] = i; if (!vis[edge[i].to]) vis[edge[i].to] = 1, que.push(edge[i].to); } } return dis[T] <= inf;}int work(int &cost){ int ans = inf; for (int i = T; i != S; i = pre[i]) ans = min(ans, edge[pre_edge[i]].f); for (int i = T; i != S; i = pre[i]) edge[pre_edge[i]].f -= ans, edge[edge[pre_edge[i]].neg].f += ans; cost += dis[T]*ans; return ans;}int mcf(int &cost){ int ans = 0; while (spfa()) ans += work(cost); return ans;}struct ep { int a, b, c, d;} epl[MAXM];int main(){ scanf("%d%d%d", &n, &m, &k); S = 1, T = n; for (int i = 1; i <= m; i++) { int a, b, c, d; scanf("%d%d%d%d", &a, &b, &c, &d); epl[i] = (ep) {a, b, c, d}; push(a, b, c, 0); } int cost = 0, ans; ans = mcf(cost); printf("%d ", ans); T++; for (int i = 1; i <= m; i++) push(epl[i].a, epl[i].b, inf, epl[i].d); push(n, n+1, k, 0); cost = 0; ans = mcf(cost); printf("%d\n", cost); return 0;}
CQOI2009 跳舞
一次舞会有n个男孩和n个女孩。每首曲子开始时,所有男孩和女孩恰好配成n对跳交谊舞。每个男孩都不会和同一个女孩跳两首(或更多)舞曲。有一些男孩女孩相互喜欢,而其他相互不喜欢(不会”单向喜欢“)。每个男孩最多只愿意和k个不喜欢的女孩跳舞,而每个女孩也最多只愿意和k个不喜欢的男孩跳舞。给出每对男孩女孩是否相互喜欢的信息,舞会最多能有几首舞曲?
把他们拆成只和喜欢的人跳舞的自己和只和不喜欢的人跳舞的自己,然后乱搞。
由于复制的时候出错导致WA*n,以后严禁复制……
#include <bits/stdc++.h>using namespace std;const int MAXN = 505, MAXM = 20005;struct node { int to, next, f, neg;} edge[MAXM];int head[MAXN], top = 0;void push(int i, int j, int k){ ++top, edge[top] = {j, head[i], k, top+1}, head[i] = top; ++top, edge[top] = {i, head[j], 0, top-1}, head[j] = top;}int vis[MAXN], bfstime = 0, lev[MAXN];int inf = 233333333;int S = 501, T = 502;queue<int> que;bool bfs(){ vis[S] = ++bfstime, lev[S] = 0, que.push(S); while (!que.empty()) { int tp = que.front(); que.pop(); //cout << tp << endl; for (int i = head[tp]; i; i = edge[i].next) { if (vis[edge[i].to] == bfstime || !edge[i].f) continue; vis[edge[i].to] = bfstime, lev[edge[i].to] = lev[tp]+1, que.push(edge[i].to); } } return vis[T] == bfstime;}int dfs(int nd, int maxf = inf){ if (nd == T || !maxf) return maxf; int ans = 0, t; for (int i = head[nd]; i; i = edge[i].next) { if (!edge[i].f || lev[edge[i].to] != lev[nd] + 1) continue; t = dfs(edge[i].to, min(maxf, edge[i].f)); ans += t, maxf -= t; edge[i].f -= t, edge[edge[i].neg].f += t; } return ans;}int dinic(){ int ans = 0; while (bfs()) ans += dfs(S); return ans;}int n, k;char str[55][55];bool judge(int a){ top = 0; memset(head, 0, sizeof head); for (int i = 1; i <= n; i++) { push(4*n+i, i, k), push(4*n+i, n+i, inf), push(2*n+i, 5*n+i, k), push(3*n+i, 5*n+i, inf); push(S, 4*n+i, a), push(5*n+i, T, a); } for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) if (str[i][j] == 'Y') push(n+i, 3*n+j, 1); else push(i, 2*n+j, 1); return dinic() == n*a;}int main(){ scanf("%d%d", &n, &k); for (int i = 1; i <= n; i++) scanf("%s", str[i]+1); int l = 0, r = 10000000; while (l <= r) { int mid = (l+r)>>1; if (judge(mid)) l = mid+1; else r = mid-1; } printf("%d\n", l-1); return 0;}
CQOI2014 危桥
Alice和Bob居住在一个由N座岛屿组成的国家,岛屿被编号为0到N-1。某些岛屿之间有桥相连,桥上的道路是双向的,但一次只能供一人通行。其中一些桥由于年久失修成为危桥,最多只能通行两次。Alice希望在岛屿al和a2之间往返an次(从al到a2再从a2到al算一次往返)。同时,Bob希望在岛屿bl和b2之间往返bn次。这个过程中,所有危桥最多通行两次,其余的桥可以无限次通行。请问Alice和Bob能完成他们的愿望吗?
其实还是比较诡异的…
如果正常建图,第一次把
定理的充分性是显然的,我们只需要证明必要性(反证)。
由于要限制次数要将ST连边的流量设为
#include <bits/stdc++.h>using namespace std;const int MAXN = 1005, MAXM = 100005;struct node { int to, next, f, neg;} edge[MAXM];int head[MAXN], top = 0;void push(int i, int j, int k){// printf("%d--%d-->%d\n", i, k, j); ++top, edge[top] = (node){j, head[i], k, top+1}, head[i] = top; ++top, edge[top] = (node){i, head[j], 0, top-1}, head[j] = top; // 反向弧}void init(){ memset(head, 0, sizeof head); top = 0;}int vis[MAXN], bfstime = 0, lev[MAXN];queue<int> que;int S = 1001, T = 1002;const int inf = 233333333;bool bfs(){ vis[S] = ++bfstime, lev[S] = 0, que.push(S); while (!que.empty()) { int tp = que.front(); que.pop(); for (int i = head[tp]; i; i = edge[i].next) { if (!edge[i].f || vis[edge[i].to] == bfstime) continue; vis[edge[i].to] = bfstime, lev[edge[i].to] = lev[tp]+1, que.push(edge[i].to); } } return vis[T] == bfstime;}int dfs(int nd, int maxf = inf){ if (nd == T || !maxf) return maxf; //cout << nd << " " << maxf << endl; int ans = 0, t; for (int i = head[nd]; i; i = edge[i].next) { if (!edge[i].f || lev[edge[i].to] != lev[nd]+1) continue; t = dfs(edge[i].to, min(maxf, edge[i].f)); ans += t, maxf -= t; edge[i].f -= t, edge[edge[i].neg].f += t; } //cout << nd << " " << ans << endl; if (maxf) lev[nd] = -1; return ans;}int dinic(){ int ans = 0; while (bfs()) ans += dfs(S); return ans;}int n, a1, a2, an, b1, b2, bn;char str[55][55];int main(){ while (scanf("%d%d%d%d%d%d%d",&n,&a1,&a2,&an,&b1,&b2,&bn)!=EOF) { a1++, a2++, b1++, b2++; for (int i = 1; i <= n; i++) scanf("%s", str[i]+1); init(); for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) if (str[i][j] == 'N') push(i, j, inf); else if (str[i][j] == 'O') push(i, j, 2); push(S, a1, an<<1), push(a2, T, an<<1); push(S, b1, bn<<1), push(b2, T, bn<<1); int d1 = dinic(); init(); for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) if (str[i][j] == 'N') push(i, j, inf); else if (str[i][j] == 'O') push(i, j, 2); push(S, a1, an<<1), push(a2, T, an<<1); push(b1, T, bn<<1), push(S, b2, bn<<1); int d2 = dinic(); //cout << d1 << " " << d2 << endl; if (d1 >= 2*(an+bn)&& d2 >= 2*(an+bn)) puts("Yes"); else puts("No"); } return 0;}
智障bug集合
SX选手的bug集合
- 把两个规模相同的数当成一个数
- 复制的时候没有i改成j
- head[i]和head[j],反向弧的流量和费用设错。
- S和SS搞混
- map中没有开long long
- 求逆元没有特判0
- 网络流学习笔记2
- 网络流学习笔记
- 网络流学习笔记
- 网络流学习笔记
- 网络流学习笔记
- 网络流学习笔记
- 网络流学习笔记(2)
- 【学习笔记】 网络流问题
- 【学习笔记】 网络流问题
- 《网络流学习笔记01》
- Java学习笔记-网络流
- [总结] 网络流学习笔记
- 网络流建模学习笔记
- 【转载】网络流学习笔记
- "网络编程"学习笔记(2)
- 网络学习笔记(2)
- 【网络流】网络流学习笔记Part1网络流基础
- 网络流之最大流学习笔记
- FTP连接树莓派(Linux)进行文件传输----需要修改vsftpd.conf文件,使能可写
- python在指定路径下获取指定类型文件路径
- HDU1147
- Red Hat Enterprise Linux 7.3
- 字符指针的可怕
- 网络流学习笔记2
- POJ2566:Bound Found(尺取法)
- Dijkstra+DFS模板总结
- kafka自定义Integer序列化类
- 自己写一个数字地球
- java设计模式--组合模式(结构型)
- 动态存储网络——问答系统
- 多态+菱形虚拟继承(下)
- sublime中pre代码片段的设置