题目链接
题意:
学校放假了,一部分学生回家一部分留校,然后有一些外校生来找他们的朋友玩。怎么安排住宿是个问题,他们所有人都有一个毛病只想睡朋友或者自己的床。问是否有一个合理的住宿方案?
看到这个很容易会想到二分图最大匹配,先将整个图分成两部分:人和床,求人和床的最大匹配。首先是本校生他才会有床,床的编号就是他的编号,然后对于每一个外校生找最大匹配。对于留校的本校生先让他睡他自己的床,还需要给他与他的床之间连一条边,刚开始想着留校生已经匹配好床了就没连结果wa了一个点,可能会存在留校生先把床让给了朋友,然后接下来的匹配他还得必须回到自己的床才能行,所以必须要连边。输入有些麻烦仔细处理一下然后就可以套板子了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=60;
int t,n,a[N],b[N],c[N][N],d[N];//d[i]:第i张床睡的谁
bool st[N];
bool find(int u)
{
for(int i=1;i<=n;i++)
{
if(!c[u][i]) continue;
int v=i;
if(!st[v]&&a[i])
{
st[v]=1;
if(!d[v]||find(d[v]))
{
d[v]=u;
return 1;
}
}
}
return 0;
}
int main()
{
cin>>t;
while(t--)
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>b[i];
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
cin>>c[i][j];
memset(d,0,sizeof d);
for(int i=1;i<=n;i++)
{
c[i][i]=a[i];//自己与床建边
if(a[i]&&!b[i]) d[i]=i;//自己先睡自己的床
}
bool flag=1;
for(int i=1;i<=n;i++)
if(!a[i])
{
memset(st,0,sizeof st);//记得清零,标记该次匹配找过谁
if(!find(i))
{
flag=0;
break;
}
}
cout<<(!flag?"T_T":"^_^")<<endl;
}
return 0;
}
下面是大佬的网络流思路:
所有的点被分为人和床(第i张床的编号定为i+n),如果i与j认识就再i与j+n之间建双向边(正边权为1,逆边权为0,下同),另外网络流都需要一个源点和汇点,分别定义为0和n*2+1(只要是不与其他点冲突的两个点都行)。因为是人要找床嘛,所以源点与所有需要找床的人之间建边,所有床与汇点之间建边,然后求出最大流再与需要找床的人数比较。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=110,M=5010;
int T,n,s,t,a[N],b[N],c[N][N];
int h[N],e[M],ne[M],w[M],idx;
void add(int a,int b,int c)
{
e[idx]=b;
w[idx]=c;
ne[idx]=h[a];
h[a]=idx++;
}
int dep[N];
bool bfs()//分层
{
queue<int> q;
memset(dep,0,sizeof dep);
dep[s]=1;
q.push(s);
while(q.size())
{
int u=q.front();
q.pop();
for(int i=h[u];i!=-1;i=ne[i])
{
int v=e[i];
if(dep[v]==0&&w[i]>0)
{
dep[v]=dep[u]+1;
q.push(v);
}
}
}
return dep[t]==0;
}
int cur[N];
int dfs(int u,int flow)
{
if(u==t) return flow;
int sum=0;
for(int i=cur[u];i!=-1;i=ne[i])
{
cur[u]=i;//弧优化
int v=e[i];
if(dep[v]==dep[u]+1&&w[i]>0)
{
int tmp=dfs(v,min(flow,w[i]));
flow-=tmp;
sum+=tmp;
w[i]-=tmp;
w[i^1]+=tmp;
if(flow==0) break;
}
}
return sum;
}
int dinic()
{
int sum=0;
while(1)
{
if(bfs()) break;
for(int i=0;i<=t;i++) cur[i]=h[i];
sum+=dfs(s,0x3f3f3f3f);
}
return sum;
}
int main()
{
cin>>T;
while(T--)
{
cin>>n;
s=0,t=(n<<1|1);
int num=0;//别忘了初始化
memset(h,-1,sizeof h);
idx=0;//记得清零
for(int i=1;i<=n;i++)
{
cin>>a[i];
if(a[i])
{
add(i+n,t,1);
add(t,i+n,0);
}
}
for(int i=1;i<=n;i++)
{
cin>>b[i];
if(!a[i]||(a[i]&&!b[i]))
{
num++;//需要找床的人数,包括留校生
add(s,i,1);
add(i,s,0);
}
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
cin>>c[i][j];
if(c[i][j]||(i==j))
{
add(i,j+n,1);
add(j+n,i,0);
}
}
int flow=dinic();
cout<<((flow<num)?"T_T":"^_^")<<endl;
}
return 0;
}