最大流总结
准备开始学习最大流。模板是在网上抄的,感觉这个看起来比较优雅。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) { for (int j = 1; j <= n; ++j) { if (ans[i][j] == 1) G[i].push_back(j), G[j].push_back(i); } } int cnt = 0; for (int i = 1; i <= n; ++i) { if (!vis[i]) { ++cnt; dfs(cnt, i); } } printf("%d\n", cnt); for (int i = 1; i <= cnt; ++i) { printf("%d", ret[i].size()); for (int j = 0; j < ret[i].size(); ++j) printf(" %d", ret[i][j]); printf("\n"); } } return 0; }
(poj好了,赶紧来到poj的题压压惊= =)
题意:有f个农场,每个农场有一个雨棚,每个农场有field[i]头牛,每个农场的雨棚能装shelter[i]头牛,然后农场之间有p条路径,路径可以通过无数头牛,但是通过路径需要花费一些时间,问所有牛都从农场走到雨棚的最短时间。
>>这题比较上面的感觉比较简单了(wa了那么多次好意思说
这竟然是我poj第100题。。。
/* 源点到每个农场连农场本来有的数量 * 雨棚到汇点连雨棚能装的数量 * 二分时间 flody求出两点之间最短时间 * 任意两点时间小于当前二分需要时间 就把两点的边加入图 * 最大流等于牛的总数则符合条件 注意不能满足输出-1 */ #include <cstdio> #include <cstring> typedef long long ll; const int N = 500; const int M = N*N; const int INF = 1 << 30; const ll inf = 1LL<<50; 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; 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; } 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; } ll d[N][N]; void floyd(int n) { for (int k = 1; k <= n; ++k) for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j) if (d[i][k] + d[k][j] < d[i][j]) d[i][j] = d[i][k] + d[k][j]; } int field[N], shelter[N]; int main() { int f, p; while (~scanf("%d%d", &f, &p)) { int sum1 = 0, sum2 = 0; for (int i = 1; i <= f; ++i) { scanf("%d%d", &field[i], &shelter[i]); sum1 += field[i]; sum2 += shelter[i]; } for (int i = 1; i <= f; ++i) { for (int j = 1; j <= f; ++j) { d[i][j] = (i==j)?0:inf; } } ll l = 0, r = 0; int a, b, c; for (int i = 1; i <= p; ++i) { scanf("%d%d%d", &a, &b, &c); if (d[a][b] > c) d[b][a] = d[a][b] = c; r += c; } if (sum1 > sum2) { puts("-1"); continue; } floyd(f); src = 2 * f + 1, sink = 2 * f + 2; ll ans = -1; while (l <= r) { ll mid = (l+r)>>1; memset(head, -1, sizeof head); cntE = 0; for (int i = 1; i <= f; ++i) { for (int j = 1; j <= f; ++j) { if (d[i][j]<=mid) addedge(i, j+f, INF); } } for (int i = 1; i <= f; ++i) { addedge(src, i, field[i]); addedge(i+f, sink, shelter[i]); } if (sap(sink) == sum1) { r = mid - 1; ans = mid; } else { l = mid + 1; } } printf("%lld\n", ans); } return 0; }
(2016.9.8
题意:2012到了,大家要逃到别的星球去。一共有n个人,m个星球,每个人只能逃到某几个星球,每个星球只能容纳一定数量的人,问能不能全部容纳。(n<10^5,m<10)
>>第一道没看题解的题QAQ
做了好几道题,很容易想到的建图是把源点到每个人连边,每个人到能去的星球连容量为1的边,每个星球到汇点连容量为星球容量的边,然后跑最大流看容量是否为n就可以了。
这样的超时的。。。
然后想到把星球状压,因为10这个数字很容易让人想到状压啊。。
想到状压之后一开始我是把所有状态连汇点的,但是wa了几次想到有点星球会重复计算,于是又把每个星球和状态连起来。
这样边数是10^5 点数也是10^5 反正是从10^6优化了一下,最大流的复杂度也不是很好算,说是O(E^2*V) ……
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 200005; const int M = 1000000; const int INF = 1 << 30; struct Edge { int from, to, next, w;//from一般用不到 } 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].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; } 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; } inline int read() { char ch = getchar(); int data = 0; while (ch < '0' || ch > '9') ch = getchar(); do { data = data * 10 + ch-'0'; ch = getchar(); } while (ch >= '0' && ch <= '9'); return data; } int a[N]; int main() { int n, m; while (~scanf("%d%d", &n, &m)) { memset(head, -1, sizeof head); cntE = 0; int st = (1<<m); src = n+m+st; sink = src+1; for (int i = 1; i <= n; ++i) addedge(src, i, 1); for (int i = 1; i <= n; ++i) { int tmp = 0; for (int j = 0; j < m; ++j) if (read()) tmp += (1<<j); if (tmp != 0) addedge(i, tmp+n+m, 1); } for (int i = 0; i < m; ++i) addedge(n+i+1, sink, a[i]=read()); for (int i = 1; i < st; ++i) for (int j = 0; j < m; ++j) if ((1<<j)&i) addedge(i+n+m, n+j+1, a[j]); if (sap(sink) == n) puts("YES"); else puts("NO"); } return 0; }
又看了下别人的题解,Orz……直接源点连状态就好了……明明可以是10^4的。。。。不过并没有快很多。。。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 200005; 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; 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; } 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; } inline int read() { char ch = getchar(); int data = 0; while (ch < '0' || ch > '9') ch = getchar(); do { data = data * 10 + ch-'0'; ch = getchar(); } while (ch >= '0' && ch <= '9'); return data; } int a[N]; int v[N]; int main() { int n, m; while (~scanf("%d%d", &n, &m)) { memset(head, -1, sizeof head); memset(v, 0, sizeof v); cntE = 0; int st = (1<<m); src = m+st; sink = src+1; for (int i = 1; i <= n; ++i) { int tmp = 0; for (int j = 0; j < m; ++j) if (read()) tmp += (1<<j); v[tmp]++; } for (int i = 0; i < m; ++i) addedge(i+1, sink, a[i]=read()); if (v[0]) { puts("NO"); continue; } for (int i = 1; i < st; ++i) { addedge(src, i+m, v[i]); for (int j = 0; j < m; ++j) if ((1<<j)&i) addedge(i+m, j+1, a[j]); } if (sap(sink) == n) puts("YES"); else puts("NO"); } return 0; }
这道题。。。要哭了真是。。。
题目中文的不说了。。。把格子用(i+j)的奇偶分开就可以变成二分图,黑白棋盘染色。。同种颜色之间不可能相交。。
然后把相邻的黑格和白格连起来,就构成了二分图,然后求最大流。
二分图的最大独立集:在图中选取最多的点,使任意所选两点均不相连
最大独立数 = 顶点数 — 最大匹配数
其实我一直都不理解这个。。这里。。最大独立点权等于点权总和-最大点权匹配。。。
然后。。。我。。。把50*50算成了250。。我从怀疑模板到怀疑人生了。。。。我花了多长时间反应过来这个错误啊。。。我提交了20多次啊。。。
#include <bits/stdc++.h> const int N = 2550; 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; 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; } 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; } int n, m; int a[100][100]; int dir[4][2] = {0, 1, 0, -1, 1, 0, -1, 0}; int get(int i, int j) { return i*m+j+1; } int main() { while (~scanf("%d%d", &n, &m)) { memset(head, -1, sizeof head); cntE = 0; int sum = 0; for (int i = 0; i < n; ++i) { for (int j = 0; j < m; ++j) { scanf("%d", &a[i][j]); sum += a[i][j]; } } src = n*m+1, sink = n*m+2; for (int i = 0; i < n; ++i) { for (int j = 0; j < m; ++j) { if ((i+j)&1) { addedge(get(i,j), sink, a[i][j]); } else { addedge(src, get(i,j), a[i][j]); for (int k = 0; k < 4; ++k) { int ni = i + dir[k][0]; int nj = j + dir[k][1]; if (ni<n&&ni>=0&&nj<m&&nj>=0) { addedge(get(i,j), get(ni,nj), INF); } } } } } printf("%d\n", sum-sap(sink)); } }
(2016.9.11
POJ2699-The Maximum Number of Strong Kings
题意:有n个人,每两个人之间都要进行一次比赛。每场比赛只有一个人胜。当一个人获得最多的获胜场数或者他打败了所有获胜场数比他多的人,他就是king,现在给出每个人的获胜次数,求最多有多少个king。n<=10。
题解:很容易想到获胜场数越多的人越容易成为king,于是我一直在想怎么贪心建图。。。。后来还是不会。。。查题解才意识到n这么小,枚举king的数量就可以了。。。。(暴力枚举所有情况都可以。。。)一个trick就是获胜局数相等的情况。。
初始化的时候出错了。。。。点数不是n。。。。。这个要记住。。。。
读入有坑。。。
#include <cstring> #include <vector> #include <cstdio> #include <queue> const int N = 200; const int INF = 1<<30; struct Edge { int to, cap, rev; }; std::vector<Edge> G[N]; int level[N]; int iter[N]; void addedge(int u, int v, int cap) { G[u].push_back(Edge{v, cap, G[v].size()}); G[v].push_back(Edge{u, 0, G[u].size()-1}); } void bfs(int s) { memset(level, -1, sizeof level); std::queue<int> que; level[s] = 0; que.push(s); while (que.size()) { int v = que.front(); que.pop(); for (int i = 0; i < G[v].size(); ++i) { Edge &e = G[v][i]; if (e.cap > 0 && level[e.to] < 0) { level[e.to] = level[v] + 1; que.push(e.to); } } } } int dfs(int v, int t, int f) { if (v == t) return f; for (int &i = iter[v]; i < G[v].size(); ++i) { Edge &e = G[v][i]; if (e.cap > 0 && level[e.to] > level[v]) { int d = dfs(e.to, t, std::min(f, e.cap)); if (d > 0) { e.cap -= d; G[e.to][e.rev].cap += d; return d; } } } return 0; } int maxflow(int s, int t) { int flow = 0; for (; ;) { bfs(s); if (level[t] < 0) return flow; memset(iter, 0, sizeof iter); int f; while ((f = dfs(s, t, INF)) > 0) flow += f; } return flow; } int a[N]; void input(int &n) { char str[100]; gets(str); int val = 0, len = strlen(str); for (int i = 0; i < len; i++) { if (str[i] >= '0' && str[i] <= '9') { val = val * 10 + str[i] - '0'; if (i == len-1 || str[i+1] < '0' || str[i+1] > '9') a[++n] = val, val = 0; } } } int main() { int T; scanf("%d", &T); getchar(); while (T--) { int n = 0; int sum = 0; int maxn = 0; input(n); for(int i = 1; i <= n; ++i) { sum += a[i]; maxn = std::max(maxn, a[i]); } //for (int i =1; i <= n; ++i) printf("%d ", a[i]); printf("%d %d\n", maxn, sum); int ans = 0; int src = 0; int sink = n*n+n+1; for (int k = 1; k <= n; ++k) { for (int i = 0; i <= sink; ++i) G[i].clear(); for (int i = 1; i <= n; ++i) { for (int j = i+1; j <= n; ++j) { int tmp = i * n + j; addedge(src, tmp, 1); addedge(tmp, i, 1); if (i < k || a[i] == a[j]) addedge(tmp, j, 1); } } for (int i = 1; i <= n; ++i) addedge(i, sink, a[i]); int tmp = maxflow(src, sink); if (tmp >= sum) { ans = n-k+1; break; } } printf("%d\n", ans); } return 0; }
#include <cstring> #include <vector> #include <cstdio> #include <queue> const int N = 1000; const int M = 1000000; const int INF = 1 << 30; struct Edge { int from, to, next, w;//from一般用不到 } 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].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; } 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; } // src--food--cow_food--cow_drink--drink--sink int main() { int n, f, d; scanf("%d%d%d", &n, &f, &d); src = 2*n+f+d+1; sink = 2*n + f + d + 2; memset(head, -1, sizeof head); cntE = 0; for (int i = 1; i <= f; ++i) addedge(src, i, 1); for (int i = 1; i <= d; ++i) addedge(f+n*2+i, sink, 1); int fi, di; int a, b; for (int k = 1; k <= n; ++k) { scanf("%d%d", &fi, &di); addedge(f+k, f+n+k, 1); for (int i = 0; i < fi; ++i) { scanf("%d", &a); addedge(a, f+k, 1); } for (int i = 0; i < di; ++i) { scanf("%d", &b); addedge(f+n+k, n*2+f+b, 1); } } printf("%d\n", sap(sink)); return 0; }
(2016.9.12
http://blog.csdn.net/hnust_xiehonghao/article/details/11050217
题意:有一条东西向流淌的河,宽为 W,河中有 N 块石头,每块石头的坐标(Xi, Yi)和最大承受人数 Ci 已知。现在有 M 个游客在河的南岸,他们想穿越这条河流,但是每个人每次最远只能跳 D 米,每跳一次耗时 1 秒。问他们能否全部穿越这条河流,如果能,最少需要多长时间。 <= N <= 50, 0 < M <= 50, 0 <= D <= 1000, 0 < W(0<= 1000, 0 < Xi < 1000, 0 < Yi < W, 0 <= Ci <= 1000)。刚看完这题,想当然的认为它是一道最小费用流问题。但是当WA之后我才明白,这题并不是去求一个给定网络的最大流,而是计算这个网络随着时间 推移每次能够留出多少流量。我们通过枚举时间的方式来决定在什么时刻能够把所有的人全部送到对岸。注意人是可以从河这岸的任意x坐标出发的。(开始人都在X轴上.对岸可以看为 Y = W的一条直线)
>>见了鬼了 CE了好多次 同样的代码 一会CE 一会运行 。。。。
这题应该是我做的这些题里面最难的了=。=
这个分解的竟然是时间。。。因为n和m都非常小,又可以知道时间最多花费n+m,否则过不去,所以枚举需要的时间就可以了。。。
每增加单位时间,就增加通往新时间的边。。。。
因为每个点都有流量限制,所以还要拆点。。。。
一直过不了样例。。。。照着别人的代码一点一点改还是不对。。。。后来终于意识到每一次的流量是要相加的(别人的dinic貌似不是这样的?。。。)。。。反正坑了好久。。
#include <cstring> #include <vector> #include <cstdio> #include <queue> using namespace std; typedef long long ll; int Scan() { int res = 0, flag = 0; char ch; if((ch = getchar()) == '-') flag = 1; else if(ch >= '0' && ch <= '9') res = ch - '0'; while((ch = getchar()) >= '0' && ch <= '9') res = res * 10 + (ch - '0'); return flag ? -res : res; } const int N = 10010; const int INF = 1<<30; struct Edge { int to, cap, rev; }; std::vector<Edge> G[N]; int level[N]; int iter[N]; void addedge(int u, int v, int cap) { G[u].push_back((Edge){v, cap, G[v].size()}); G[v].push_back((Edge){u, 0, G[u].size()-1}); } void bfs(int s) { memset(level, -1, sizeof level); std::queue<int> que; level[s] = 0; que.push(s); while (que.size()) { int v = que.front(); que.pop(); for (unsigned i = 0; i < G[v].size(); ++i) { Edge &e = G[v][i]; if (e.cap > 0 && level[e.to] < 0) { level[e.to] = level[v] + 1; que.push(e.to); } } } } int dfs(int v, int t, int f) { if (v == t) return f; for (int &i = iter[v]; i < (int)G[v].size(); ++i) { Edge &e = G[v][i]; if (e.cap > 0 && level[e.to] > level[v]) { int d = dfs(e.to, t, std::min(f, e.cap)); if (d > 0) { e.cap -= d; G[e.to][e.rev].cap += d; return d; } } } return 0; } int maxflow(int s, int t) { int flow = 0; for (; ;) { bfs(s); if (level[t] < 0) return flow; memset(iter, 0, sizeof iter); int f; while ((f = dfs(s, t, INF)) > 0) flow += f; } return flow; } int x[55], y[55], c[55]; int dis[55][55]; int n, m, d, w; int cal(int a, int b) { return (x[a]-x[b])*(x[a]-x[b]) + (y[a]-y[b])*(y[a]-y[b]); } int get1(int x, int t) { return (t-1) * n + x + 1; } int get2(int x, int t) { return 5000 + (t-1) * n + x + 1; } int main() { n = Scan(); m = Scan(); d = Scan(); w = Scan(); for (int i = 1; i <= n; ++i) x[i] = Scan(), y[i] = Scan(), c[i] = Scan(); if (w <= d) { puts("1"); return 0; } for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j) dis[i][j] = (cal(i, j) <= d*d) ? 1 : 0; int ans = 0; int src = 0, sink = 1; for (int t = 1; t <= n+m; ++t) { for (int i = 1; i <= n; ++i) { if (y[i] <= d) addedge(src, get1(i, t), INF); if (y[i]+d >= w) addedge(get2(i,t), sink, INF); addedge(get1(i, t), get2(i, t), c[i]); for (int j = 1; j <= n; ++j) if (dis[i][j]) addedge(get2(i, t), get1(j, t+1), INF); } ans += maxflow(src, sink); if (ans >= m) { printf("%d\n", t+1); break; } } if (ans < m) puts("IMPOSSIBLE"); return 0; }
先刷到这,有时间还要做套最小割的题= =