连接:http://codevs.cn/problem/1069/
题意:
将n名犯人分成两部分,只有处在同一部分中的罪犯之间会产生冲突,
问通过划分所能得到的最大的一组冲突(两个犯人之间) 的最小值。
思路:
因为候选答案集合具有单调性,所以可以考虑二分答案。
假设mid为当前可以产生的最大值,那么凡是相互矛盾<=mid
的犯人可以待在一个屋里,而>mid的犯人应该待在不同屋里,
即在矛盾之>mid的犯人之间连边。理论上如果mid可行,那么
最后会产生一张二分图。所以判定二分图是否成立即可,然后
二分到最小可行解。
时间复杂度:o(logM*(M+N))=o(14*(10^5+0.2*10^5))
AC代码:
(注意二分答案,答案序列中的0也可以作为最终结果,其次注意mid和边权a【mid】的区别)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=20010;
const int M=100001*2;
int Next[M];
int ver[M];
int head[N];
int edge[M];
int tot;
void add(int x,int y,int z)
{
ver[++tot]=y;
Next[tot]=head[x];
edge[tot]=z;
head[x]=tot;
}
int nc[M];
int vc[M];
int hc[N];
int ec[M];
int tc;
void add_c(int x,int y)
{
vc[++tc]=y;
nc[tc]=hc[x];
hc[x]=tc;
}
int color[N];
void init(int mid)
{
memset(hc,0,sizeof(hc));
memset(vc,0,sizeof(vc));
memset(nc,0,sizeof(nc));
tc=0;
memset(color,0,sizeof(color));
for(int i=1;i<=tot;i+=2)
{
if(edge[i]>mid)
{
add_c(ver[i],ver[i+1]);
add_c(ver[i+1],ver[i]);
}
}
}
bool dfs(int x,int c)
{
color[x]=c;
for(int i=hc[x];i;i=nc[i])
{
int y=vc[i];
if(color[y]==c)return 0;
if(!color[y]&&dfs(y,-c)==false)return 0;//不用担心往回跑的情况
}
return 1;
}
int a[N];
int main()
{
int n,m;
while(cin>>n>>m)
{
memset(head,0,sizeof(head));
memset(ver,0,sizeof(ver));
memset(Next,0,sizeof(Next));
tot=0;
int cnt=0;
a[++cnt]=0;//有可能是0;
for(int i=1;i<=m;++i)
{
int x,y,z;
cin>>x>>y>>z;
add(x,y,z);
add(y,x,z);
a[++cnt]=z;
}
sort(a+1,a+1+cnt);
int p=1,q=cnt,mid;
int ans=0;
while(p<=q)
{
mid=(p+q)/2;
init(a[mid]);
bool flag=1;
for(int i=1;i<=n;++i)
{
if(color[i]==0&&!dfs(i,1))
{
flag=0;
break;
}
}
if(flag==0)p=mid+1;
else ans=mid,q=mid-1;
}
cout<<a[ans]<<endl;
}
return 0;
}
The end;