网络流:HDU 3605

HDU 3605

hdu 3605

Sample Input
1 1
1
1

2 2
1 0
1 0
1 1

Sample Output
YES
NO

题意:n个人,m个星球,然后给你一个n*m的矩阵,如果第i行第j列为1,说明第i个人可以居住在第j个星球上,0则说明不能。最后一行m个数据,表示对应的星球可以容纳的最大人数。问你最后能不能找到一种方法可以让所有人都有地方住。

解题思路:很明显的最大流的题目,1<=n<=1e5,1<=m<=10。就常规的建图会卡RE和卡T,所以需要进行缩点,状态压缩(对这种感觉不是很敏感,可惜。。),因为最多只有10个星球,那么每个人可能有的选择只有2^10种,而n有1e5,说明会有很多重复的人,那这些重复的人实际上是没有区别的。所以我们选择缩点,对选择完全的一样的人进行合并,这样就等于最多只用建1024个点和源点连接了,大大减少了边的数量,流量就为个数就行了,后面的就常规了,可以连的星球就连,星球再与汇点连边流量为限制的最多人数就可以了。这种匹配的题用匈牙利匹配也可以做。

完整代码:
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn=1005;
const int inf=0x3f3f3f3f;
struct Edge{
    int u,v,c;
    int next;
}edge[maxn*20];
int n,m;
int edn;//边数
int head[maxn*20],dis[maxn*20];
int sp,tp;//原点,汇点

void addedge(int u,int v,int c)
{
    edge[edn].u=u; edge[edn].v=v; edge[edn].c=c;
    edge[edn].next=head[u]; head[u]=edn++;

    edge[edn].u=v; edge[edn].v=u; edge[edn].c=0;
    edge[edn].next=head[v]; head[v]=edn++;
}
int bfs()
{
    queue <int> q;
    memset(dis,-1,sizeof(dis));
    dis[sp]=0;
    q.push(sp);
    while(!q.empty())
    {
        int cur=q.front();
        q.pop();
        for(int i=head[cur];i!=-1;i=edge[i].next)
        {
            int u=edge[i].v;
            if(dis[u]==-1 && edge[i].c>0)
            {
                dis[u]=dis[cur]+1;
                q.push(u);
            }
        }
    }
    return dis[tp] != -1;
}
int dfs(int a,int b)
{
    int r=0;
    if(a==tp) return b;
    for(int i=head[a];i!=-1 && r<b;i=edge[i].next)
    {
        int u=edge[i].v;
        if(edge[i].c>0 && dis[u]==dis[a]+1)
        {
            int x=min(edge[i].c,b-r);
            x=dfs(u,x);
            r+=x;
            edge[i].c-=x;
            edge[i^1].c+=x;
        }
    }
    if(!r)dis[a]=-2;
    return r;
}

int dinic(int sp,int tp)
{
    int total=0,t;
    while(bfs())
    {
        while(t=dfs(sp,inf))
        total+=t;
    }
    return total;
}
int a[1050];
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        edn=0;
        memset(head,-1,sizeof(head));
        memset(a,0,sizeof(a));
        sp=0,tp=1024+m+1;
        for(int i=1;i<=n;i++)
        {
            int q=0;
            for(int j=1;j<=m;j++)
            {
                int x;
                scanf("%d",&x);
                if(x==1)
                   q=q|(1<<(j-1));
            }
            a[q]++;
        }
        for(int i=1;i<=1024;i++)
        {
            if(a[i])
                addedge(sp,i,a[i]);
            for(int j=1;j<=m;j++)
            {
                if(i&(1<<(j-1)))
                    addedge(i,1024+j,a[i]);
            }
        }
        for(int i=1;i<=m;i++)
        {
            int x;
            scanf("%d",&x);
           // addedge(n+i,n+i+m,x);
            addedge(i+1024,tp,x);
        }
        int ans=dinic(sp,tp);
        if(ans==n)
            printf("YES\n");
        else
            printf("NO\n");
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值