HDU - 3605 Escape (缩点+最大流/二分图多重匹配)

题意:有N(1<=N<=1e5)个人要移民到M(1<=M<=10)个星球上,每个人有自己想去的星球,每个星球有最大承载人数。问这N个人能否移民成功。

分析:可以用最大流的思路求解该问题,新建源点和汇点,源点与人间加入弧,流量为他想去的星球之和;星球和汇点间加入弧,流量为其承载数量;人和星球间加入弧,流量无限。但是本题中N很大,M却很小,所以想去星球的编号组成的二进制数最多不超过1024,那么可以将N个人缩点。然后跑最大流,满流为N。

#include<iostream>
#include<stdio.h>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int MAXN =1e4+5,maxm =1e6+5;
const int INF=0x3f3f3f3f;

struct ISAP{
    int head[MAXN], nv, n, tot;         //nv:编号修改的上限
    int num[MAXN], d[MAXN], pre[MAXN], cur[MAXN], q[MAXN];
    struct node{
        int v, next, cap;
    }edge[maxm];
    void init(){
        memset(head,-1,sizeof(head));
        tot=0;
    }
    void AddEdge(int u, int v, int cap){
        edge[tot].v=v;edge[tot].cap=cap;edge[tot].next=head[u];
        head[u]=tot++;
        edge[tot].v=u;edge[tot].cap=0;edge[tot].next=head[v];
        head[v]=tot++;
    }
    void bfs(int s,int t){
        memset(num,0,sizeof(num));
        memset(d,-1,sizeof(d));
        int f1=0, f2=0, i;
        q[f1++]=t;
        d[t]=0;
        num[0]=1;
        while(f1>=f2){
            int u=q[f2++];
            for(i=head[u];i!=-1;i=edge[i].next){
                int v=edge[i].v;
                if(d[v]==-1){
                    d[v]=d[u]+1;
                    num[d[v]]++;
                    q[f1++]=v;
                }
            }
        }
    }
    int isap(int s,int t){
        memcpy(cur,head,sizeof(cur));
        int flow=0, i, u=pre[s]=s;
        bfs(s,t);
        while(d[s]<nv){
            if(u==t){
                int f=INF, pos;
                for(i=s;i!=t;i=edge[cur[i]].v){
                    if(f>edge[cur[i]].cap){
                        f=edge[cur[i]].cap;
                        pos=i;
                    }
                }
                for(i=s;i!=t;i=edge[cur[i]].v){
                    edge[cur[i]].cap-=f;
                    edge[cur[i]^1].cap+=f;
                }
                flow+=f;
                if(flow>=n)
                    return flow;
                u=pos;
            }
            for(i=cur[u];i!=-1;i=edge[i].next)
                if(d[edge[i].v]+1==d[u]&&edge[i].cap)   break;
            if(i!=-1){
                cur[u]=i;
                pre[edge[i].v]=u;
                u=edge[i].v;
            }
            else{
                if(--num[d[u]]==0) break;
                int mind=nv;
                for(i=head[u];i!=-1;i=edge[i].next){
                    if(mind>d[edge[i].v]&&edge[i].cap){
                        mind=d[edge[i].v];
                        cur[u]=i;
                    }
                }
                d[u]=mind+1;
                num[d[u]]++;
                u=pre[u];
            }
        }
        return flow;
    }
}F;

int vis[1200];

int main()
{
    #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
        freopen("out.txt","w",stdout);
    #endif
    int S,T,N,M,u,v,tmp,k;
    int op;
    while(scanf("%d%d",&N,&M)==2){
        F.init();
        k=0;
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=N;++i){
            int x=0;
            for(int j=1;j<=M;++j){
                scanf("%d",&op);
                x<<=1;
                x+=op;                      //转换为二进制状态
            }
            vis[x]++;
        }
        for(int i=1;i<=1050;++i) if(vis[i]) k++;        //记录缩点后点数
        S=0;T=k+M+1; F.nv=k+M+2,F.n = N;
        int id=0;
        for(int i=1;i<=1050;++i){
            if(!vis[i]) continue;
            id++;
            int sta = i,cnt = vis[i];
            for(int j=1,t=1;j<=sta;j<<=1,t++){
                if(j&sta) F.AddEdge(id,t+k,INF);
            }
            F.AddEdge(S,id,cnt);
        }
        for(int i=1;i<=M;++i){
            scanf("%d",&tmp);
            F.AddEdge(i+k,T,tmp);
        }
        if(F.isap(S,T)==N) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}

还有一种做法是二分图多重匹配。二分图多重匹配的思路与匈牙利算法接近,也是从以匹配的点中寻找增广路。X部为N个人,Y部为M个星球,每个星球有自己的匹配上限。在时间上,两种实现方法所差不多。

#include<iostream>
#include<stdio.h>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long LL;
const int maxn = 1e5+5,maxm = 2e6+5;
int N,M;
struct Node{
    int K[15];
}link[maxn];
int cnt[15];
struct Edge{
    int to,next;
}edges[maxm];
int head[maxn],tot;
bool used[15];
int limit[15];

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

void AddEdge(int u,int v)
{
    edges[tot].to = v;
    edges[tot].next = head[u];
    head[u] = tot++;
}

bool dfs(int u){
    int v;
    for(int i=head[u];~i;i = edges[i].next){
        v = edges[i].to;
        if(!used[v]){
            used[v]=true;
            if(cnt[v]<limit[v]){
                link[v].K[cnt[v]++]=u;
                return true;
            }
            for(int j=0;j<cnt[v];++j){
                if(dfs(link[v].K[j])){
                    link[v].K[j]=u;
                    return true;
                }
            }
        }
    }
    return false;
}

bool hungary(){
    memset(cnt,0,sizeof(cnt));
    for(int u=1;u<=N;u++){
        memset(used,0,sizeof(used));
        if(!dfs(u)) return false;     //只要有一个人不能匹配则失败
    }
    return true; 
}

int main()
{
    #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
        freopen("out.txt","w",stdout);
    #endif
    int T,u,v,tmp,k;
    int op;
    while(scanf("%d%d",&N,&M)==2){
        init();
        for(int i=1;i<=N;++i){
            for(int j=1;j<=M;++j){
                scanf("%d",&op);
                if(op) AddEdge(i,j);    
            }
        }
        for(int i=1;i<=M;++i) scanf("%d",&limit[i]);
        if(hungary()) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}

 

posted @ 2018-07-27 09:53  xiuwenL  阅读(180)  评论(0编辑  收藏  举报