题目链接:HDU-3605 Escape
题意
地球上有$n(1leq n leq 100000)$个人需要移居到$m(1leq m leq 10)$个外星球上,每个人只有特定的若干个外星球可供选择,每个外星球有接受移居的人数上限,问能否让所有人移居成功。
思路
由于$n$的范围有$1e5$,直接建图的话边可能达到$1e6$条,跑最大流会超时。
考虑到$m$最大只有$10$,每个人可选择的若干星球最多只有$2^{10}$种可能的状态,所以实际上当$n$比较大时,很多人可选择的若干个星球是一样的。我们可以用二进制状态压缩来表示一种可选星球的状态,统计出这种状态的人数,以状态为结点,就不用每个人用一个结点表示。
用$s$和$t$分别表示源点、汇点,$(始点,终点,容量)$表示一条边,建图如下:
对于每一个状态,连边$(s,状态,可选星球是这个状态的人数)$;
对于一个状态中可选择的每一个星球,连边$(状态,星球,无穷大)$;
对于每一个星球,连边$(星球,t,星球的人数上限)$。
最大流就是能成功移居的最大人数。
代码实现
#include <iostream> #include <cstdio> #include <cstring> #include <queue> using std::queue; const int INF = 0x3f3f3f3f, N = 2000, M = 30000; int head[N], d[N], cnt[N]; int s, t, tot, maxflow; struct Edge { int to, cap, nex; } edge[M]; queue<int> q; void add(int x, int y, int z) { edge[++tot].to = y, edge[tot].cap = z, edge[tot].nex = head[x], head[x] = tot; edge[++tot].to = x, edge[tot].cap = 0, edge[tot].nex = head[y], head[y] = tot; } bool bfs() { memset(d, 0, sizeof(d)); while (q.size()) q.pop(); q.push(s); d[s] = 1; while (q.size()) { int x = q.front(); q.pop(); for (int i = head[x]; i; i = edge[i].nex) { int v = edge[i].to; if (edge[i].cap && !d[v]) { q.push(v); d[v] = d[x] + 1; if (v == t) return true; } } } return false; } int dinic(int x, int flow) { if (x == t) return flow; int rest = flow, k; for (int i = head[x]; i && rest; i = edge[i].nex) { int v = edge[i].to; if (edge[i].cap && d[v] == d[x] + 1) { k = dinic(v, std::min(rest, edge[i].cap)); if (!k) d[v] = 0; edge[i].cap -= k; edge[i^1].cap += k; rest -= k; } } return flow - rest; } void init(int n) { tot = 1, maxflow = 0; s = n, t = s + 1; memset(head, 0, sizeof(head)); memset(cnt, 0, sizeof(cnt)); } int main() { int n, m; while (~scanf("%d %d", &n, &m)) { init(1024 + m); for (int i = 0, sta = 0; i < n; i++, sta = 0) { for (int j = 0, ok; j < m; j++) { scanf("%d", &ok); sta = sta * 2 + ok; } if (!cnt[sta]) for (int j = m - 1; j >= 0; j--) { if ((sta >> j) & 1) add(sta, 1023 + m - j, INF); } cnt[sta]++; } for (int i = 0; i < 1024; i++) if (cnt[i]) add(s, i, cnt[i]); for (int i = 0, num; i < m; i++) { scanf("%d", &num); add(i + 1024, t, num); } while (bfs()) maxflow += dinic(s, INF); if (maxflow >= n) puts("YES"); else puts("NO"); } return 0; }