HDU-3605 Escape(状态压缩+最大流求多重匹配、改版匈牙利算法)

84 篇文章 6 订阅
20 篇文章 0 订阅

题意:

n个人,m个星球,每个人只愿意往特定的星球搬迁,而每个星球有一个最大允许居住数量。问能否将每个人都安排好居住的星球。(n <= 1e5, m <= 10)

思路:

其实就是裸的二分图的多重匹配,但是由于n较大,直接做肯定会超时。但发现m较小,所以我们先通过m将所有人的状态压缩成2^10个,然后用网络流就可以做了。

还有一种改版的匈牙利做法。即我们记录一下被匹配的点(即B部的点)已经匹配了多少个点,它能够匹配的最大数量就是它的上限。如果此时不足上限,那么就直接匹配成功并记录下来,如果达到上限,那么我们就像匈牙利最初的算法那样去尝试已经匹配了这个点的所有的点能否去匹配新的点。如此递归,由于m很小,所以最深顶多达到m个深度,即复杂度为O(n*m);


代码1:

#include <algorithm>      
#include <iostream>      
#include <string.h>      
#include <cstdio>      
#include <queue>      
using namespace std;      
const int inf = 0x3f3f3f3f;      
const int maxn = 1100;      
const int maxm = 25000;    
struct node{int w; int v, next;} edge[maxm];      
int pre[maxn], rec[maxn], head[maxn], gap[maxn], now[maxn];      
int dis[maxn];    
int t, n, m, no, up;      
int S, T;     
queue<int> q;    
inline void add(int u, int v, int w)      
{      
    edge[no].v = v; edge[no].w = w;      
    edge[no].next = head[u]; head[u] = no++;      
    edge[no].v = u; edge[no].w = 0;      
    edge[no].next = head[v]; head[v] = no++;      
}      
inline void pre_init()      
{      
    no = 0;    
    memset(head, -1, sizeof head);      
}      
void init(int S, int T)      
{      
    memset(gap, 0, sizeof gap);      
    memset(dis, 0x3f, sizeof dis);      
    for(int i = 0; i <= up; ++i)       
    now[i] = head[i];    
    while(!q.empty()) q.pop();      
    dis[T] = 0; q.push(T);      
    while(!q.empty())     
    {      
        int tp = q.front(); q.pop();      
        ++gap[dis[tp]];      
        int k = head[tp];      
        while(k != -1)      
        {      
            if(dis[edge[k].v] == inf && edge[k^1].w)      
            {      
                dis[edge[k].v] = dis[tp]+1;      
                q.push(edge[k].v);      
            }      
            k = edge[k].next;      
        }      
    }      
}      
int SAP(int S, int T)      
{    
    int ans = 0, flow = inf;    
    int top = S;      
    pre[S] = S; init(S, T);      
    while(dis[S] < up)    
    {    
        if(top == T)      
        {      
            ans += flow;      
            while(top != S)    
            {      
                edge[rec[top]].w -= flow;      
                edge[rec[top]^1].w += flow;      
                top = pre[top];      
            }      
            flow = inf;      
        }      
        int k = now[top];      
        while(k != -1)      
        {      
            int v = edge[k].v;      
            if(edge[k].w && dis[top] == dis[v]+1)      
            {      
                flow = min(flow, edge[k].w);      
                pre[v] = top; rec[v] = k;      
                now[top] = k; top = v;      
                break;      
            }      
            k = edge[k].next;      
        }      
        if(k == -1)      
        {      
            int mind = up;      
            if(--gap[dis[top]] == 0) break;    
            int k = now[top] = head[top];    
            while(k != -1)      
            {      
                if(edge[k].w && mind>dis[edge[k].v]) mind = dis[edge[k].v];      
                k = edge[k].next;      
            }      
            ++gap[dis[top] = mind+1];      
            top = pre[top];      
        }      
    }      
    return ans;      
}
int dig[1<<10];
void mapping()    
{    
    int x, a, b;
    memset(dig, 0, sizeof dig);
	for(int i = 1; i <= n; ++i)
	{
		int t = 0;
		for(int j = 0; j < m; ++j)
		{
			scanf("%d", &x);
			if(x) t += (1<<j);
		}
		++dig[t];
	}
	for(int i = 0; i < (1<<10); ++i)
	{
		if(!dig[i]) continue;
		add(S, i, dig[i]);
		int t = i, k = 0;
		while(t)
		{
			++k;
			if(t&1) add(i, (1<<10)+k, dig[i]);
			t >>= 1;
		}
	}
    for(int i = 1; i <= m; ++i)  
    {
        scanf("%d", &x);
        add((1<<10)+i, T, x); 
    }   
}    
int main()      
{
    while(scanf("%d %d", &n, &m) != EOF)      
    {  
        up = (1<<10)+12, S = 1035, T = 1036;    
        pre_init();      
        mapping();
        if(SAP(S, T) == n) puts("YES");  
        else puts("NO");   
    }      
    return 0;      
}


代码2:

#include <bits/stdc++.h>
using namespace std;
bool G[100005][15];
int match[15][100005];
int vis[15];
int hav[15], maxx[15], cun[15][100005];
int n, m;
bool dfs(int u)
{
	for(int i = 1; i <= m; ++i)
	{
		if(!G[u][i] || vis[i]) continue;
		vis[i] = 1;
		if(hav[i] < maxx[i])
		{
			cun[i][++hav[i]] = u;
			return true;
		}
		else
		{
			for(int j = 1; j <= hav[i]; ++j)
			if(dfs(cun[i][j]))
			{
				cun[i][hav[i]] = u;
				return true;
			}
		}
	}
	return false;
} 
bool work()
{
	memset(hav, 0, sizeof hav); 
	for(int i = 1; i <= n; ++i)
	{
		memset(vis, 0, sizeof vis);
		if(!dfs(i)) return false;
	}
	return true;
}
int main()
{
	while(scanf("%d %d", &n, &m) != EOF)
	{
		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", &maxx[i]);
		if(work()) puts("YES");
		else puts("NO");
	}
	return 0;
}


继续加油~

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值