P2055 [ZJOI2009]假期的宿舍[二分图匹配]

题目描述

学校放假了 · · · · · · 有些同学回家了,而有些同学则有以前的好朋友来探访,那么住宿就是一个问题。

比如 A 和 B 都是学校的学生,A 要回家,而 C 来看B,C 与 A 不认识。我们假设每个人只能睡和自己直接认识的人的床。那么一个解决方案就是 B 睡 A 的床而 C 睡 B 的床。而实际情况可能非常复杂,有的人可能认识好多在校学生,在校学生之间也不一定都互相认识。

我们已知一共有 n 个人,并且知道其中每个人是不是本校学生,也知道每个本校学生是否回家。问是否存在一个方案使得所有不回家的本校学生和来看他们的其他人都有地方住。

解析

看一眼,哦吼,二分图最大匹配没得跑了。

显然留在学校没走的人(包括在校学生和外来人)都要与一张床匹配,而在校学生(包括走了的)都有床。那么我们可以把所有在学校的人当作左部节点,把所有床当作右部节点,如果最大匹配\(=\)留在学校的学生人数,该方案可行。

匈牙利或者最大流,我写的最大流,练练手。

参考代码

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#define N 51
#define M 10010
#define INF 0x3f3f3f3f
using namespace std;
inline int read()
{
    int f=1,x=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}
struct rec{
    int next,ver,leng;
}g[M<<1];
int head[M],tot,d[M],n,num;
int is[N],home[N];
inline void add(int x,int y,int val)
{
    g[++tot].ver=y,g[tot].leng=val;
    g[tot].next=head[x],head[x]=tot;
}
inline bool bfs()
{
    memset(d,0,sizeof(d));
    queue<int> q;
    d[0]=1;q.push(0);
    while(q.size()){
        int x=q.front();q.pop();
        for(int i=head[x];i;i=g[i].next){
            int y=g[i].ver,z=g[i].leng;
            if(!z||d[y]) continue;
            d[y]=d[x]+1;
            q.push(y);
            if(y==n*2+1) return 1;
        }
    }
    return 0;
}
inline int dinic(int x,int flow)
{
    if(x==n*2+1) return flow;
    int rest=flow;
    for(int i=head[x];i;i=g[i].next){
        int y=g[i].ver,z=g[i].leng;
        if(!z||d[y]!=d[x]+1) continue;
        int k=dinic(y,min(rest,z));
        if(!k) d[y]=0;
        else{
            g[i].leng-=k;
            g[i^1].leng+=k;
            rest-=k;
        }
    }
    return flow-rest;
}
int main()
{
    int T;
    T=read();
    while(T--){
        memset(head,0,sizeof(head));
        memset(is,0,sizeof(is));//在校学生为 1 
        memset(home,0,sizeof(home));//回家的人为 1 
        tot=1,num=0;
        n=read();
        for(int i=1;i<=n;++i){
            is[i]=read();
            if(is[i]) add(i+n,n*2+1,1),add(n*2+1,i+n,0);
        }
        for(int i=1;i<=n;++i){
            home[i]=read();
            if(!home[i]||!is[i]) add(0,i,1),add(i,0,0),num++;
        }
        for(int i=1;i<=n;++i)
            for(int j=1;j<=n;++j){
                int x=read();
                if(j>i) continue;
                if(i==j&&!home[i]) add(i,i+n,1),add(i+n,i,0);
                else if(x) add(i,j+n,1),add(j+n,i,0),add(j,i+n,1),add(i+n,j,0);
            }
        int now,ans=0;
        while(bfs())
            while((now=dinic(0,INF))) ans+=now;
        if(ans==num) printf("^_^\n");
        else printf("T_T\n");
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/DarkValkyrie/p/11425697.html