HDU - 3605 Escape(最大流+状态压缩)

本文介绍了一种解决特定匹配问题的方法——状态压缩最大流算法。该算法适用于当选项数量较少的情况,通过将每个人的选择用二进制表示,并统计相同选择的人数作为边的容量,最终通过求最大流判断是否每个人都能前往心仪的星球。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

点击查看原题
题目大意:
有n个人m个星球,每个人都有自己中意的星球(可以多个),每个星球有居住的人数上限。问是否可以每个人都去到自己中意的星球。
分析:
看起来是裸的最大流问题,也果断交了一发(TLE,人数过多),上网参考了一下大牛的状态压缩想法,因为星球最多只有10个所以可以把每个人的状态看成是2^m。然后统计同一种方案的人数作为容量。
建图:
源点链接被选用了的方案 容量为选择人数
方案链接位为1的星球 容量为inf
星球链接汇点 容量为限制人数
跑最大流看是否等于n

AC代码

#include<stdio.h>
#include<string.h>
#include<vector>
#include<set>
#include<queue>
#define inf 100000000
using namespace std;
struct tree
{
    int from,to,cap,flow;
    tree(){}
    tree(int ff,int tt,int cc,int ww)
    {
        from=ff;to=tt;cap=cc;flow=ww;
    }
};
int n,m;
vector<tree> G;
vector<int> v[20000];
int cur[20000];
int que[2000000];
int d[20000];
int sink,source;
void addtree(int ff,int tt,int cc)
{
    G.push_back(tree(ff,tt,cc,0));
    G.push_back(tree(tt,ff,0,0));
    int m=G.size();
    v[ff].push_back(m-2);
    v[tt].push_back(m-1);
}

bool bfs()
{
    memset(d,-1,sizeof(d));
    int front=0,back=0;
    que[back++]=source;
    d[source]=0;
    while(front<back)
    {
        int p=que[front++];
        if(p==sink) return true;
        for(int i=0;i<v[p].size();i++)
        {
            tree &e=G[v[p][i]];
            if(d[e.to]==-1&&e.flow<e.cap)
            {
                d[e.to]=d[p]+1;
                que[back++]=e.to;
            }
        }
    }
    return false;
}
int dfs(int x,int a)
{
    if(x==sink||a==0)
    return a;
    int flow=0,f;
    for(int &i=cur[x];i<v[x].size();i++)
    {
        tree &e=G[v[x][i]];
        if(d[x]+1==d[e.to]&&(f=dfs(e.to,min(a,e.cap-e.flow)))>0)
        {
            e.flow+=f;
            G[v[x][i]^1].flow-=f;
            flow+=f;
            a-=f;
            if(a==0)
            break;
        }
    }
    if(!flow) d[x]=-2;
    return flow;
}
int maxflow()
{
    int flow=0;
    //printf("!!!");
    while(bfs())
    {
        //printf("!!");
        memset(cur,0,sizeof(cur));
        flow+=dfs(source,inf);
    }
    return flow;
}
int peo[2000];
int main()
{
    while(scanf("%d%d",&n,&m)==2)
    {
        memset(peo,0,sizeof(peo));
        for(int i=0;i<20000;i++)
        v[i].clear();
        G.clear();
        source=1,sink=3000;
        for(int i=0;i<(1<<m);i++)
        {
            int t=i;
            for(int j=m-1;j>=0&&t;j--)
            {
                if(t&1) addtree(i+2,2000+j,inf);
                t>>=1;
            }
        }
        for(int i=0;i<n;i++)
        {
            int t=0;
            for(int j=0;j<m;j++)
            {
                int aa;
                scanf("%d",&aa);
                t<<=1;
                t+=aa;
            }
            peo[t]++;
        }
        for(int i=0;i<(1<<m);i++)
        {
            if(peo[i]) addtree(source,i+2,peo[i]);
        }
        //printf("!!!\n");
        for(int i=0;i<m;i++)
        {
            int aa;
            scanf("%d",&aa);
            addtree(2000+i,sink,aa);
        }
        //printf("!!!");
        if(n==maxflow())
        printf("YES\n");
        else
        printf("NO\n");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值