题目大意:
算了太长了还是不讲了吧,想看题的戳这里
对于这道题,我们可以用网络流做(不懂网络流的戳这里),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");
}
}