假期的宿舍

##题目大意:
有n个人,分别是:在校的学生,回家的学生,来看望在校学生的学生,有人走了,就有空床,有人来了,就需要床,只能睡自己的床或者直接认识的人的床.
读入有些烦,要仔细看:
第一行一个数 T 表示数据组数。接下来 T 组数据,每组数据第一行一个数n 表示涉及到的总人数。接下来一行 n 个数,第 i 个数表示第 i 个人是否是在校学生 (0 表示不是,1 表示是)。再接下来一行 n 个数,第 i 个数表示第 i 个人是否回家 (0 表示不回家,1 表示回家,注意如果第 i 个人不是在校学生,那么这个位置上的数是一个随机的数,你应该在读入以后忽略它)。接下来 n 行每行 n 个数,第 i 行第 j 个数表示 i 和 j 是否认识 (1 表示认识,0 表示不认识,第 i 行 i 个的值为 0,但是显然自己还是可以睡自己的床),认识的关系是相互的。

##解题思路:
话说这道题还挺难的.
Codevs还是最高段位(大师)
可以用匈牙利算法,我用Dinic(网络流——最大流)做的.
首先建立源点,汇点,源点连接需要床的人,汇点连接床,然后把人和他可以睡的床连接,再求最大流就可以了.
最重要的是初始化,因为是多组数据

##源程序:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#define inf 2147483647
using namespace std;
int Q,n,m,s,t,cnt,last[2001],dis[2001],school[101],home[101],sum;
struct node{int to,next,c;}
e[2005];
queue<int> que;//队列
void add(int u,int v,int c)//邻接表
{
    
    e[cnt].to=v;e[cnt].c=c;e[cnt].next=last[u];last[u]=cnt++;
    e[cnt].to=u;e[cnt].c=0;e[cnt].next=last[v];last[v]=cnt++;
}
bool bfs()//bfs找增广路,分层
{
    memset(dis,-1,sizeof(dis));//初始化
    while (!que.empty()) que.pop();//清空队列
    dis[s]=0; que.push(s);//源点进入队列
    while (!que.empty())
    {
        int u=que.front();que.pop();
        //把最先进入队列的赋值给u然后出队列
        for (int i=last[u];i!=-1;i=e[i].next)
        //找增广路,分层
        if (e[i].c&&dis[e[i].to]==-1)
        {
            dis[e[i].to]=dis[u]+1;
            if (e[i].to==t) return 1;
            //如果到了汇点就说明这是一条增光路呗
            que.push(e[i].to);
            //当然要进入队列
        }
    }
    return 0;
}
int dfs(int x,int other)
{
    if (x==t||!other) return other;
    int ret=0;//ret是已用流量,other是总共有的流量
    for (int i=last[x];i!=-1;i=e[i].next) 
    //修改增广路的流量
        if (e[i].c&&dis[e[i].to]==dis[x]+1)
        {
            int f=dfs(e[i].to,min(e[i].c,other-ret));
            //接着改
            if (!f) dis[e[i].to]=-1;
            e[i].c-=f;
            e[i^1].c+=f;
            //改流量
            ret+=f;
            //改已用流量
        }
    if(!ret) dis[x]=-1;
    return ret;
}
int dinic()

{
    int ans=0;
    while (bfs()) ans+=dfs(s,inf);
    //找增光路,改流量,计算答案
    return ans;
}
int main()
{
    scanf("%d",&Q);
    for (int q=1;q<=Q;q++)
    {
    	scanf("%d",&n);
    	cnt=0;
    	memset(&e,0,sizeof(e));
    	memset(last,-1,sizeof(last));
    	s=n<<1|1; t=s+1; sum=0;//初始化
    	for (int i=1;i<=n;i++)
    	 {scanf("%d",&school[i]);if(school[i]) add(i+n,t,1);}//读入,建边
    	for (int i=1;i<=n;i++)
    	 {scanf("%d",&home[i]);if(!school[i]||!home[i]) add(s,i,1),sum++;}
    	 //读入,建边,sum统计需要床的人
    	int flag;
    	for (int i=1;i<=n;i++)
    	 for (int j=1;j<=n;j++)
    	  {
          	scanf("%d",&flag);
          	if(flag||i==j) add(i,j+n,1);
          	//读入关系,建边
    	  }
    	int ans=dinic();//赋值
        if (sum==ans) printf("^_^\n");
        else printf("T_T\n");
        //判定是否可以让所有需要床的人都有床睡并输出
    }
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值