ZOJ-3933 Team Formation (二分图最佳完美匹配)

题目大意:n个人,分为两个阵营。现在要组成由若干支队伍,每支队伍由两个人组成并且这两个人必须来自不同的阵营。同时,每个人都有m个厌恶的对象,并且厌恶是相互的。相互厌恶的人不能组成一支队伍。问最多能组成多少支队伍,并在在队伍数最多的前提下求最多的女生数以及输出方案。

题目分析:很显然是个二分图。从阵营1的每个元素向阵营0的每个元素连一条有向边(互相厌恶的不连),这就得到一张二分图。给每条边一个权值,边两端的女生越多,权值越大。只需要求最大完美匹配即可。

 

ps:比赛的时候连边只连了一半儿,导致一直wa。。。

 

代码如下:

# include<iostream>
# include<cstdio>
# include<cstring>
# include<vector>
# include<queue>
# include<list>
# include<set>
# include<map>
# include<string>
# include<cmath>
# include<cstdlib>
# include<algorithm>
using namespace std;
# define LL long long

const int N=1005;
const int INF=1000000000;
const LL oo=0x7fffffffffffffff;
const double eps=1e-10;

int n;
int mp[N/2+5][N/2+5];
int slack[N/2+5];
int link[N/2+5];
int lx[N/2+5];
int ly[N/2+5];
int s[N/2+5],t[N/2+5];
int vis[N/2+5][N/2+5];
string p,q;

bool match(int x)
{
	s[x]=1;
	for(int y=0;y<n;++y){
		if(t[y]) continue;
		int temp=lx[x]+ly[y]-mp[x][y];
		if(temp==0){
			t[y]=1;
			if(link[y]==-1||match(link[y])){
				link[y]=x;
				return true;
			}
		}else if(slack[y]>temp)
			slack[y]=temp;
	}
	return false;
}

void update()
{
	int d=INF;
	for(int i=0;i<n;++i) if(!t[i])
		d=min(d,slack[i]);
	for(int i=0;i<n;++i) if(s[i]) lx[i]-=d;
	for(int i=0;i<n;++i){
		if(t[i]) ly[i]+=d;
		else slack[i]-=d;
	}
}

void KM()
{
	memset(link,-1,sizeof(link));
	memset(ly,0,sizeof(ly));
	for(int i=0;i<n;++i){
		lx[i]=-1;
		for(int j=0;j<n;++j)
			lx[i]=max(lx[i],mp[i][j]);
	}
	for(int i=0;i<n;++i){
		fill(slack,slack+n,INF);
		while(true){
			memset(s,0,sizeof(s));
			memset(t,0,sizeof(t));
			if(match(i))
				break;
			update();
		}
	}
}

int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		cin>>p>>q;
		memset(mp,0,sizeof(mp));
		memset(vis,0,sizeof(vis));
		for(int i=0;i<n;++i){
			int k;
			scanf("%d",&k);
			while(k--){
				int a;
				scanf("%d",&a);
				vis[i][a-1]=vis[a-1][i]=1;
			}
			for(int j=0;j<n;++j){
				if(i==j||p[i]==p[j]||vis[i][j]||vis[j][i]) continue;
				int gg=1;      ///这里如果为gg=0会wa
				if(q[i]=='0') ++gg;
				if(q[j]=='0') ++gg;
				if(p[i]=='1'&&p[j]=='0') mp[i][j]=gg;
				else if(p[i]=='0'&&p[j]=='1') mp[j][i]=gg;
			}
		}
		KM();
		int ansa=0,ansb=0;
		for(int i=0;i<n;++i) if(link[i]!=-1&&mp[link[i]][i]){
			++ansa;
			if(q[link[i]]=='0') ++ansb;
			if(q[i]=='0') ++ansb;
		}
		printf("%d %d\n",ansa,ansb);
		for(int i=0;i<n;++i) if(link[i]!=-1&&mp[link[i]][i])
			printf("%d %d\n",link[i]+1,i+1);
	}
	return 0;
}

  

posted @ 2016-04-30 20:43  20143605  阅读(342)  评论(0编辑  收藏  举报