[ZJOI2009]假期的宿舍 题解

题目大意:

算了太长了还是不讲了吧,想看题的戳这里

对于这道题,我们可以用网络流做(不懂网络流的戳这里),1~n记录需要床的人,与原点连一条边,n+1~2*n记录每个学生的床,与汇点连一条边,然后跑一次最大流,原点拥有的流量为需要床的人的数量,假如全部流量都可以流到汇点,说明存在可行方案,思路就是这么简单,上代码吧:

#include <cstdio>
#include <cstring>

int n,m,t,len;
struct node{int x,y,z,next;};
node e[10010];
int first[110];
bool a[110],b[110];
int tou,wei;
void buildroad(int x,int y,int z)
{
    len++;
    e[len].x=x;
    e[len].y=y;
    e[len].z=z;
    e[len].next=first[x];
    first[x]=len;
}
int h[110],q[110],st,ed;
bool bfs()
{
    memset(h,0,sizeof(h));
    st=1,ed=2;
    q[st]=tou;
    h[tou]=1;
    while(st!=ed)
    {
        int x=q[st];
        for(int i=first[x];i;i=e[i].next)
        {
            int y=e[i].y;
            if(h[y]==0&&e[i].z>0)
            {
                h[y]=h[x]+1;
                q[ed++]=y;
            }
        }
        st++;
    }
    if(h[wei]==0)return false;
    else return true;
}
int minn(int x,int y){return x<y?x:y;}
int dfs(int x,int z)
{
    if(x==wei)return z;
    int tt=0;
    for(int i=first[x];i;i=e[i].next)
    {
        int y=e[i].y;
        if(z-tt<=0)
            break;
        if(h[y]==h[x]+1&&e[i].z>0)
        {
            int my=dfs(y,minn(z-tt,e[i].z));
            tt+=my;
            e[i].z-=my;
            e[(i^1)].z+=my;//这种反向边的表示方式我在写网络流的那篇博客中有讲,不明白的可以去看看 
        }
    }
    if(tt==0)h[x]=0;
    return tt;
}

int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            int x;
            scanf("%d",&x);
            if(x)a[i]=true;
            else a[i]=false;
        }
        for(int i=1;i<=n;i++)
        {
            int x;
            scanf("%d",&x);
            if(x)b[i]=true;
            else b[i]=false;
        }
        int m=n;//记录需要床的人数 
        memset(first,0,sizeof(first));len=1;//记得初始化 
        tou=2*n+1,wei=tou+1;
        for(int i=1;i<=n;i++)
        {
            if(a[i]&&b[i])m--;//假如有在校学生回家那么需要床的人-1,其他的都是要床的 
            if(a[i]&&!b[i])buildroad(i,i+n,1),buildroad(i+n,i,0);//假如在校学生不回家,那么他是可以睡自己的床的 
            if((a[i]&&!b[i])||!a[i])buildroad(tou,i,1),buildroad(i,tou,0);//假如这是个不回家的在校学生或者是外人,说明他需要床,于是与原点连一条边 
            if(a[i])buildroad(i+n,wei,1),buildroad(wei,i+n,0);//如果这个人是在校学生,说明他有床,把他的床和汇点连一条边 
            for(int j=1;j<=n;j++)
            {
                int x;
                scanf("%d",&x);
                if(x)//如果i和j认识 
                {
                    if(a[i]&&(!a[j]||(a[j]&&!b[j])))buildroad(j,i+n,1),buildroad(i+n,j,0);//如果i有床并且j需要床 
                    if(a[j]&&(!a[i]||(a[i]&&!b[i])))buildroad(i,j+n,1),buildroad(j+n,i,0);//同上 
                }
            }
        }
        int ans=0;
        while(bfs())ans+=dfs(tou,m);//标准网络流 
        if(ans!=m)printf("T_T\n");
        else printf("^_^\n");
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值