【训练题】分队 P1672

Description

每年的CQOI比赛结束后都会组织选手参加一次游玩活动。由于人数众多,通常会把选手分成两队出发。组委会为了和谐,想尽量把比较熟悉的选手分在同一队。我们用“友好度”(一个正整数值)来表示某两位选手之间的熟悉程度,友好度越大,则两名选手越熟悉。而“和谐度”定义为两队中熟悉程度最低的那对选手的友好度。


现在告诉你选手数量和一些选手之间的友好度,该如何分队(每组至少1人),才能让两队的和谐度达到最大。请你来帮助组委会完成这个任务。


Input

第一行为两个正整数n,分别选手的数目以及友好度大于0的选手对数,选手编号为1n。,选手编号为1n。

  
接下来是一个n*n的矩阵,矩阵的第i行第j列表示选手i和选手j的友好度c,当i==j时c=0,注意,矩阵一定是斜对称的。


Output

一个整数,表示最大和谐度,某些情况下可能为0。


Hint

2<=n<=1000


Solution

首先这道题是一道需要二分猜答案的二分图练习题。需要求的是最大和谐度,也就是最小友好度,那么就用邻接矩阵存每一段友好度(也就是每一条边的权值),dfs的时候将大于猜的那个答案的值的边跳过,小于这个值的边染色为1和2,判断小于这个答案的所有边是否能构成二分图。然后因为这个图不一定连通所以当vis等于0的时候就需要dfs一次。


定义一个L和R,把L赋初值为0,R赋初值为midx,当mid满足条件时就往右边猜,往右边猜就要把L更新成mid+1,否则就往左边猜,把R更新成mid-1,最后当L=R的时候while循环退出输出ans,程序结束。


注意事项:
1.g[s][q]>=mid和s==q的时候都需要跳过,只有到遇到vis的值为1且两个端点颜色相等的时候就return false,或者它的子图不连通的时候return false。
2.每一次check(mid)之前要把color数组和vis数组清零避免爆了(。)
3.猜答案的时候跳L和R要+1和-1

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define maxn 1005
using namespace std;
int n,midx,midn,ans;
bool flag;
int g[maxn][maxn],color[maxn];
bool vis[maxn];
void init(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			scanf("%d",&g[i][j]);
		}
	}
}
bool dfs(int s,int c,int mid){
	color[s]=c;
	vis[s]=true;
	for(int q=1;q<=n;q++){
		if(g[s][q]>=mid)continue;
		if(s==q)continue; 
		if(vis[q]){
			if(color[s]==color[q])return false;
		}
		else if(dfs(q,3-c,mid)==false)return false;
	}
	return true;
}
void tomid(){
	for(int q=1;q<=n;q++){
		for(int p=1;p<=n;p++){
			midx=max(midx,g[q][p]);
		}
	}
	midn=0;
}
bool check(int mid){
    memset(color,0,sizeof(color));
    memset(vis,0,sizeof(vis));
	for(int i=1;i<=n;i++)
		if(!vis[i])
			if(!dfs(i,1,mid)) return false;
	return true;
}
int main(){
	init();
	tomid();
	int mid=(midx+midn)/2;
	int L=0,R=midx;
    while(L<=R){
    	mid=(L+R)>>1;
    	if(check(mid)){
    		ans=mid;
    		L=mid+1;
		}
		else R=mid-1;
	}
	printf("%d\n",ans);
	return 0;
}
posted @ 2018-11-30 16:37  虚拟北方virtual_north。  阅读(230)  评论(0编辑  收藏  举报