1. 程式人生 > >hdu 3605 Escape【網路流+狀態壓縮】

hdu 3605 Escape【網路流+狀態壓縮】

有n個人要移居m個星球,給出每個合適的星球,每個星球最多能容納的人數,問是否所有人都可以移居。(1≤N≤105,1≤M≤10)(1≤N≤105,1≤M≤10) 

最多有十個星球,而N很大,所以可能會有很多重複,因為每個人去哪個星球最多有2^10中情況,所以可以壓縮點,把情況相同的人放到一塊,在進行網路流就行;

這道題中,要建立超級源點 0 和超級匯點 (多少種情況+m+1);

鄰接表:

#include<cstdio>
#include<cstring>
#include<string>
#include<stdlib.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#include<map>
#include<vector>
#include<stack>
#define inf 0x3f3f3f3f
#include<queue>
using namespace std;
typedef long long ll;
const int N=1050;

struct node
{
    int u,v,flow,cal;
}q;

vector<node>edge;//記錄邊
vector<int>head[N];//記錄每個邊的下標(可自行模擬)
int a[N],pre[N],peo[N];
int n,m;

void init(int mm)
{
    edge.clear();
    for(int i=0;i<=mm;i++)
        head[i].clear();
}

void add(int u,int v,int flow)
{
    edge.push_back(node{u,v,0,flow});
    edge.push_back(node{v,u,0,0});
    int tmp=edge.size();
    head[u].push_back(tmp-2);
    head[v].push_back(tmp-1);
}

int solve(int s,int t)//EK演算法
{
    int ans=0;
    queue<int>que;
    while(true)
    {
        que.push(s);
        memset(a,0,sizeof(a));
        a[s]=inf;
        while(!que.empty())
        {
            int u=que.front();
            que.pop();
            for(int k=0;k<head[u].size();k++)
            {
                q=edge[head[u][k]];
                if(!a[q.v]&&q.cal>q.flow)
                {
                    que.push(q.v);
                    pre[q.v]=head[u][k];
                    a[q.v]=min(a[u],q.cal-q.flow);
                }
            }
        }
        if(!a[t]) break;
        for(int v=t;v!=s;v=edge[pre[v]].u)
        {
            edge[pre[v]].flow+=a[t];
            edge[pre[v]^1].flow-=a[t];
        }
        ans+=a[t];
    }
    if(ans==n)
        return true;
    return false;
}

int main()
{
    while(~scanf("%d %d",&n,&m))
    {
        memset(peo,0,sizeof(peo));
        int temp,p,po=0;
        for(int i=0;i<n;i++)
        {
            temp=0;
            for(int j=0;j<m;j++)
            {
                temp<<=1;
                scanf("%d",&p);
                temp+=p;//二進位制統計情況是否相同
            }
            if(!peo[temp])
                po++;//記錄最多有多少情況
            peo[temp]++;
        }
        int t=po+m+1;//匯點,即所有星球連結一個超級匯點
        init(t);
        for(int i=1;i<=m;i++)
        {
            scanf("%d",&p);
            add(po+i,t,p);//建立從星球到匯點的邊
        }
        p=0;
        for(int i=0;i<(1<<m);i++)
        {
            if(peo[i])
            {
                add(0,++p,peo[i]);//建立源點到每種情況的邊
                int tmp=i,f=1;
                while(tmp)
                {
                    if(tmp&1)
                        add(p,po+f,peo[i]);//建立每種情況到它們可以去的星球的邊
                    f++;
                    tmp>>=1;
                }
            }
        }
        if(solve(0,t))
            printf("YES\n");
        else
            printf("NO\n");
    }
    return 0;
}