[ZJOI2009]假期的宿舍

洛咕

题意:学校放假了 · · · · · · 有些同学回家了,而有些同学则有以前的好朋友来探访,那么住宿就是一个问题.比如 A 和 B 都是学校的学生,A 要回家,而 C 来看B,C 与 A 不认识.我们假设每个人只能睡和自己直接认识的人的床.那么一个解决方案就是 B 睡 A 的床而 C 睡 B 的床.而实际情况可能非常复杂,有的人可能认识好多在校学生,在校学生之间也不一定都互相认识.我们已知一共有 n 个人,并且知道其中每个人是不是本校学生,也知道每个本校学生是否回家.问是否存在一个方案使得所有不回家的本校学生和来看他们的其他人都有地方住.

分析:网络流难就难在建图了.本题中可以看成一个二分图,左部分(A)是要睡床的人(如果是在校学生且回家,就不在该集合A中,否则都在),右部分(B)是床(数量为在校学生的人数,非本校学生本来是没有床位的).如果(i,j)认识就连边(i->j)的床,容量为1.当然,自己也要连自己的床.

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
const int N=100005;
int n,s,t,max_flow,sum,sum1;
int a[N],b[N],A[N],B[N],c[105][105];
int visit[N],incf[N],pre[N];
int tot,head[N],nxt[N],to[N],w[N];
inline void add(int a,int b,int c){
	nxt[++tot]=head[a];head[a]=tot;to[tot]=b;w[tot]=c;
	nxt[++tot]=head[b];head[b]=tot;to[tot]=a;w[tot]=0;
}
inline bool bfs(){
	memset(visit,0,sizeof(visit));
	queue<int>q;q.push(s);visit[s]=1;incf[s]=1e9;
	while(q.size()){
		int u=q.front();q.pop();
		for(int i=head[u];i;i=nxt[i]){
			if(!w[i])continue;
			int v=to[i];if(visit[v])continue;
			incf[v]=min(incf[u],w[i]);
			pre[v]=i;q.push(v);visit[v]=1;
			if(v==t)return 1;
		}
	}
	return 0;
}
inline void update(){
	int x=t;
	while(x!=s){
		int i=pre[x];
		w[i]-=incf[t];
		w[i^1]+=incf[t];
		x=to[i^1];
	}
	max_flow+=incf[t];
}
int main(){
	int T=read();
	while(T--){
		n=read();tot=1;max_flow=0;sum=0;sum1=0;//初始化
		memset(head,0,sizeof(head));
		for(int i=1;i<=n;++i){
			a[i]=read();
			if(!a[i])A[++sum]=i;//非本校学生是一定要睡床的,加入左部分
		}
		for(int i=1;i<=n;++i){
			b[i]=read();
			if(a[i]&&!b[i])A[++sum]=i;//本校学生且不回家也是要睡床的
		}
		for(int i=1;i<=n;++i)if(a[i])B[++sum1]=i;//只有在校学生才有床位	
		for(int i=1;i<=n;++i)
			for(int j=1;j<=n;++j)
				c[i][j]=read();
		for(int i=1;i<=sum;++i)
			for(int j=1;j<=sum1;++j)
				if(c[A[i]][B[j]]||A[i]==B[j])add(i+1,j+sum+1,1);//建二分图
		s=1;t=sum+sum1+2;
		for(int i=1;i<=sum;++i)add(s,i+1,1);		
		for(int j=1;j<=sum1;++j)add(j+sum+1,t,1);//二分图变网络流
		while(bfs())update();
		if(max_flow==sum)printf("^_^
");
		else printf("T_T
");
	}
    return 0;
}

原文地址:https://www.cnblogs.com/PPXppx/p/11614802.html