zoukankan      html  css  js  c++  java
  • HDU-3605 Escape(最大流,状态压缩)

    题目链接: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;
    }
    View Code
    作者:_kangkang
    本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。
  • 相关阅读:
    Android 杀掉当前程序的进程
    Android Studio 2.3更换默认的ConstraintLayout布局
    Android ConstraintLayout约束控件链接整理
    Java 实现字符串反转
    Android 网络技术HTTP
    数据库框架 Litepal
    Android FastJson解析
    Java类的初始化顺序
    Android 免费短信获取国家列表和国家代码
    【转】仿Android 联系人SideBar排序,根据拼音A-Z字母快速导航,以及输入搜索条件过滤,显示姓名的文字图片
  • 原文地址:https://www.cnblogs.com/kangkang-/p/11336515.html
Copyright © 2011-2022 走看看