hdu 3605 Escape 多重匹配模板 (Hungary ) | 最大流

28 篇文章 0 订阅
23 篇文章 0 订阅

N个人M个星球,每个人都有适合居住的星球,每个星球有最大的容纳人数。求是否行星能满足住人。

相比二分图求最大匹配,变化在link数组的应用,及对行星人数限制这个条件寻找增广轨,而这里的增广轨即 ”原先已经找好居住行星的人需要找新的行星“ 这一过程。跑一次Hungary即可。注意这里要用c++否则会T。神奇的输入挂可减很多时间

#include<stdio.h>
#include<string.h>
#include<algorithm>

using namespace std;

const int N = 100005;

int vis[12];
int link[12][N];	//link[i][j]为第i颗行星上的第j个人 
int c[15], v[15];	//第I颗行星能容纳的最大人数,当前的人数 
int n, m;
int a[N][12];		//mat 

int read()
{
    char ch = ' ';
    int ans = 0;
    while( ch < '0' || ch > '9')
        ch = getchar();
    while(ch <= '9' && ch >= '0')
    {
        ans = ans * 10 + ch - '0';
        ch = getchar();
    }
    return ans;
}

int dfs( int u )
{
	for( int i = 0; i < m; i++ )
	{
		if( !vis[i] && a[u][i] )
		{
			vis[i] = 1;
			if( v[i] < c[i] )	//第I颗行星还能住人 
			{
				link[i][v[i]++] = u;	//记录要住的人的编号 
				return 1;
			}
			else
			{
				for( int j = 0; j < v[i]; j++ )	//为已经住在第i颗行星上的人找新行星(有可能重新找的还是原来的行星) 
				{
					if( dfs(link[i][j]) )
					{
						link[i][j] = u;	//腾出j位置给u这个人 
						return 1;
					}
				}
			}
		}
		
	}
	return 0;
}

int main()
{
	while(~scanf("%d%d", &n, &m))
	{
		memset(v, 0, sizeof(v));
		for( int i = 0; i < n; i++ )
			for( int j = 0; j < m; j++ )
				a[i][j] = read();
		for( int i = 0; i < m; i++ )
			c[i] = read();
		bool OK = 0;
		for( int i = 0; i < n; i++ )
		{
			memset(vis, 0, sizeof(vis));
			if( !dfs(i) )
			{
				OK = 1;
				break;
			}
		}
		printf("%s\n", OK ? "NO" : "YES");
	}
	return 0;
}

邻接表版本的。(时间差在输入那里= =)

#include<stdio.h>
#include<string.h>
#include<algorithm>

using namespace std;

const int N = 100005;

int vis[12];
int link[12][N];	//link[i][j]为第i颗行星上的第j个人 
int c[15], v[15];	//第I颗行星能容纳的最大人数,当前的人数 
int n, m;
int head[N];
int a[N][12];

struct node{
	int to, nxt;
}e[N * 10];
int cnt;

void init()
{
	cnt = 0;
	memset(head, -1, sizeof(head));
	memset(v, 0, sizeof(v));
}

int read()
{
    char ch = ' ';
    int ans = 0;
    while( ch < '0' || ch > '9')
        ch = getchar();
    while(ch <= '9' && ch >= '0')
    {
        ans = ans * 10 + ch - '0';
        ch = getchar();
    }
    return ans;
}

void add(int u, int to ) 
{
	e[cnt].to = to;
	e[cnt].nxt = head[u];
	head[u] = cnt++;
}

int dfs( int u )
{
	for( int i = head[u]; ~i; i = e[i].nxt )
	{
		int to = e[i].to;
		if( !vis[to] )
		{
			vis[to] = 1;
			if( v[to] < c[to] )
			{
				link[to][v[to]++] = u;
				return 1;
			}
			else
			{
				for( int j = 0; j < v[to]; j++ )
				{
					if( dfs(link[to][j]) )
					{
						link[to][j] = u;
						return 1;
					}
				}
			}
		}
	}
	return 0;
}

/*int dfs( int u )
{
	for( int i = 0; i < m; i++ )
	{
		if( !vis[i] && a[u][i] )
		{
			vis[i] = 1;
			if( v[i] < c[i] )	//第I颗行星还能住人 
			{
				link[i][v[i]++] = u;	//记录要住的人的编号 
				return 1;
			}
			else
			{
				for( int j = 0; j < v[i]; j++ )	//为已经住在第i颗行星上的人找新行星(有可能重新找的还是原来的行星) 
				{
					if( dfs(link[i][j]) )
					{
						link[i][j] = u;	//腾出j位置给u这个人 
						return 1;
					}
				}
			}
		}
		
	}
	return 0;
}*/

int main()
{
	while(~scanf("%d%d", &n, &m))
	{
		int x;
		init();
		for( int i = 0; i < n; i++ )
			for( int j = 0; j < m; j++ )
			{
				x = read();
				//scanf("%d", &x);
				if( x )
					add(i, j);
			}
		for( int i = 0; i < m; i++ )
			c[i] = read();
			//scanf("%d", &c[i]);
		bool OK = 0;
		for( int i = 0; i < n; i++ )
		{
			memset(vis, 0, sizeof(vis));
			if( !dfs(i) )
			{
				OK = 1;
				break;
			}
		}
		printf("%s\n", OK ? "NO" : "YES");
	}
	return 0;
}


至于最大流,很明显可以看出来,但是按照 超源 -> 星球 -> 人 ->超汇这种裸建边的话,图大小达到1e6,稳T的。所以我们可以将图缩一下。

因为根据人有1e5,而星球只有10,那么对于人的选择来说,一共有2^10 = 1024个选择,远小于人数,也就是说有很多人的选择是一样的,那么我们利用状压把相同选择的人划到一个集合里面,最后根据这个集合里面的人对对应星球连边即可。

、、终于用最大流过了它哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈

#include <map>
#include <set>
#include <queue>
#include <stack>
#include <vector>
#include <cmath>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <algorithm>

using namespace std;

#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
#define ls rt << 1
#define rs rt << 1 | 1
#define pi acos(-1.0)
#define eps 1e-8
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int N = 10100;

struct node{
    int v, w, nxt;
    int cap;
}e[N*10];

int head[N];
int dep[N];
int gap[N];
int cur[N];
int pp[N];
int s[N], top;
queue <int> q;
int a[N*10];

int n, m, nn;
int cnt;
int st, ed;

void init()
{
    memset( a, 0, sizeof( a ) );
    memset( pp, 0, sizeof( pp ) );
    memset( head, -1, sizeof( head ) );
    cnt = 0;
    st = (1<<m)+m, ed = st+1;
}

void add( int u, int v, int w, int rw = 0 )
{
    e[cnt].v = v;
    e[cnt].w = w;
    e[cnt].nxt = head[u];
    head[u] = cnt++;

    e[cnt].v = u;
    e[cnt].w = 0;
    e[cnt].nxt = head[v];
    head[v] = cnt++;
}

void rev_bfs( )
{
    memset( gap, 0, sizeof( gap ) );
    memset( dep, -1, sizeof( dep ) );
    while( !q.empty() ) q.pop();
    q.push( ed );
    dep[ed] = 0;
    gap[0] = 1;
    while( q.empty() ) {
        int u = q.front();
        q.pop();
        for( int i = head[u]; ~i; i = e[i].nxt ) {
            int v = e[i].v;
            if( ~dep[v] )
                continue;
            dep[v] = dep[u] + 1;
            gap[dep[u]]++;
            q.push( v );
        }
    }
}

int isap()
{
    memcpy( cur, head,  sizeof cur );
    rev_bfs();
    int flow = 0, i, u = st, nv = ed+1;
    top = 0;
    while( dep[st] < nv ) {
        if( u == ed ) {
            int tmp, minn = inf;
            for( i = 0; i < top; ++i ) {
                if( minn > e[s[i]].w ) {
                    minn = e[s[i]].w;
                    tmp = i;
                }
            }
            for( i = 0; i < top; ++i ) {
                e[s[i]].w -= minn;
                e[s[i]^1].w += minn;
            }
            flow += minn;
            top = tmp;
            u = e[s[top]^1].v;
        }
        for( i = cur[u]; ~i; i = e[i].nxt ) {
            if( e[i].w > 0 && dep[u] == dep[e[i].v] + 1 ) {
                cur[u] = i;
                break;
            }
        }
        if( ~i ) {
            s[top++] = cur[u];
            u = e[i].v;
        }
        else {
            if( 0 == (--gap[dep[u]]) )
                break;
            int minn = nv;
            for( i = head[u]; ~i; i = e[i].nxt ) {
                int v = e[i].v;
                if( e[i].w > 0 && minn > dep[v] ) {
                    minn = dep[v];
                    cur[u] = i;
                }
            }
            dep[u] = minn + 1;
            gap[dep[u]]++;
            if( u != st ) {
                u = e[ s[--top]^1 ].v;
            }
        }
    }
    return flow;
}

int read(){//输入优化
    char ch = ' ';
    while(ch < '0' || ch > '9') ch = getchar();
    int x = 0;
    while(ch >= '0' && ch <= '9'){
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x;
}

int main()
{
    while( ~scanf("%d%d", &n, &m) )
    {
        init();
        for( int i = 0; i < n; ++i )
        {
            int x = 0;
            for( int j = 0; j < m; ++j )
            {
                int tt;
                //scanf("%d", &tt);
                tt = read();
                if( tt )
                    x |= (1 << j);
            }
            pp[x]++;
        }
        for( int i = 0; i < ( 1 << m ); ++i )
        {
            if( pp[i] )
            {
                add( st, i, pp[i] );
                for( int j = 0; j < m; ++j )
                {
                    if( i & ( 1 << j ) )
                        add( i, j+(1<<m), pp[i] );
                }
            }
        }
        for( int i = 0; i < m; ++i )
        {
            int x;
            //scanf("%d", &x);
            x = read();
            if( x )
                add( i+(1<<m), ed, x );
        }
        int ans = isap();
        //printf("%d\n", ans);
        if( ans >= n )
            puts("YES");
        else
            puts("NO");
    }
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值