HDU 3605 Escape (状压+最大流 | 二分图多重匹配)

题意:n个人,m个星球,每个人给出对哪些星球感兴趣,每个星球给出能住的人数,求能否让所有人都住上感兴趣的星球。

More set of test data, the beginning of each data is n (1 <= n <= 100000), m (1 <= m <= 10) n indicate there n people on the earth, m representatives m planet, planet and people labels are from 0. Here are n lines, each line represents a suitable living conditions of people, each row has m digits, the ith digits is 1, said that a person is fit to live in the ith-planet, or is 0 for this person is not suitable for living in the ith planet.
The last line has m digits, the ith digit ai indicates the ith planet can contain ai people most…
0 <= ai <= 100000

题解:状压+最大流 | 二分图多重匹配
最大流做法:
在这里插入图片描述
n很大,直接最大流会t。
m最大为10,状态最多为1024,考虑状压,将每个人感兴趣的星球压缩成一个点,与源点建边,容量为感兴趣的星球数量。
每个状态与该状态所含的星球建边,容量为inf即可。
星球与汇点建边,容量为星球的最大人数。

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<fstream>
#include<set>
#include<map>
#include<sstream>
#include<iomanip>
#define ll long long
using namespace std;
//SAP邻接表形式 注意初始化
const int MAXN = 20010;
const int MAXM = 200010;
const int INF = 0x3f3f3f3f;
struct Node {
    int to, next, cap;
}edge[MAXM];
int tol;
int head[MAXN];
int gap[MAXN], dis[MAXN], pre[MAXN], cur[MAXN];
void init() {
    tol = 0;
    memset(head, -1, sizeof(head));
}
void addedge(int u, int v, int w, int rw = 0) {
    edge[tol].to = v; edge[tol].cap = w; edge[tol].next = head[u]; head[u] = tol++;
    edge[tol].to = u; edge[tol].cap = rw; edge[tol].next = head[v]; head[v] = tol++;
}
int sap(int start, int end, int nodenum) {
    memset(dis, 0, sizeof(dis));
    memset(gap, 0, sizeof(gap));
    memcpy(cur, head, sizeof(head));
    int u = pre[start] = start, maxflow = 0, aug = -1;
    gap[0] = nodenum;
    while (dis[start] < nodenum) {
    loop:
        for (int& i = cur[u]; i != -1; i = edge[i].next) {
            int v = edge[i].to;
            if (edge[i].cap && dis[u] == dis[v] + 1) {
                if (aug == -1 || aug > edge[i].cap)
                    aug = edge[i].cap;
                pre[v] = u;
                u = v;
                if (v == end) {
                    maxflow += aug;
                    for (u = pre[u]; v != start; v = u, u = pre[u])
                    {
                        edge[cur[u]].cap -= aug;
                        edge[cur[u] ^ 1].cap += aug;
                    }
                    aug = -1;
                }
                goto loop;
            }
        }
        int mindis = nodenum;
        for (int i = head[u]; i != -1; i = edge[i].next) {
            int v = edge[i].to;
            if (edge[i].cap && mindis > dis[v]) {
                cur[u] = i;
                mindis = dis[v];
            }
        }
        if ((--gap[dis[u]]) == 0)break;
        gap[dis[u] = mindis + 1]++;
        u = pre[u];
    }
    return maxflow;
}
int n, m, x, sum[1111];
int main() {
    while (~scanf("%d%d", &n, &m)) {
        init();
        memset(sum, 0, sizeof(sum));
        int flag = 1;
        for (int i = 1; i <= n; i++) {
            int tmp = 0;
            for (int j = 1; j <= m; j++) {
                scanf("%d", &x);
                tmp = (tmp << 1) + x;
            }
            if (tmp == 0) flag = 1;
            sum[tmp]++;
        }
        int tot = 0;
        for (int i = 0; i < 1024; i++) {
            if (!sum[i]) continue;
            addedge(0, i + 1, sum[i]);
            for (int j = 0; j < m; j++) {
                if (i & (1 << j)) addedge(i + 1, 1024 + j + 1, sum[i]); //容量inf
            }
        }
        for (int i = 1; i <= m; i++) {
            scanf("%d", &x);
            addedge(1024 + i, 1024 + m + 1, x);
        }
        if (sap(0, 1024 + m + 1, 1024 + m + 2) == n) puts("YES");
        else puts("NO");
    }
	return 0;
}

二分图多重匹配做法:
在这里插入图片描述
套个板子就行。

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<fstream>
#include<set>
#include<map>
#include<sstream>
#include<iomanip>
#define ll long long
using namespace std;
//SAP邻接表形式 注意初始化
const int MAXN = 100010;
const int MAXM = 20;
int uN, vN;
int g[MAXN][MAXM];
int linker[MAXM][MAXN];
bool used[MAXM];
int num[MAXM];//右边最大的匹配数
bool dfs(int u) {
    for (int v = 1; v <= vN; v++)
        if (g[u][v] && !used[v]) {
            used[v] = true;
            if (linker[v][0] < num[v]) {
                linker[v][++linker[v][0]] = u;
                return true;
            }
            for (int i = 1; i <= num[v]; i++)
                if (dfs(linker[v][i])) {
                    linker[v][i] = u;
                    return true;
                }
        }
    return false;
}
bool hungary() {
    int res = 0;
    for (int i = 1; i <= vN; i++)
        linker[i][0] = 0;
    for (int u = 1; u <= uN; u++) {
        memset(used, false, sizeof(used));
        if (!dfs(u)) return false;
    }
    return true;
}
int n, m, x;
int main() {
    while (~scanf("%d%d", &n, &m)) {
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                scanf("%d", &g[i][j]);
            }
        }
        for (int i = 1; i <= m; i++) {
            scanf("%d", &num[i]);
        }
        uN = n, vN = m;
        if (hungary()) puts("YES");
        else puts("NO");
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值