SOl1:开虚点的方法
并查集的精义在于物以类聚,信息的传递性。
即A认识B,B认识C,则A认识C。
对于本题1与2有冲突,那我们自然要将它们分开才行。
如果1又与3冲突,那3自然要与2放在一个房间了。至于是放哪个房间,不是重点。
发现每个人与其它人的关系,只有两种即有冲突,没有冲突。
于是我们可以对每个人开两个集合出来,一个集合放与之没有冲突的,另一个集合放与之有冲突的。
这样便引出的虚点的概念来了。
于是1开出1号点,与1+N号点,2开出2号点,与2+N号点。
接下来1与2有冲突,于是1与N+2号点放一个集合,2与N+1号点放一个集合。这样就完整的表述了1与2有冲突这个事情。
于是这样一直放下去,直到当发现发生某个冲突的两个人,居然在一起集合里面(在一个集合说明它们是和平相处的),则说明这个冲突是无法避免的了。
/* 此题还可以用二分答案加二分图染色的方法来做 二分答案是指二分一下,前多少条冲突规则是可以接受的 二分图染色是说,对于一个图,我们将一个点染成黑色,则这条边的 另一个点染成白色。反之亦然,如果染好一个点后,发现与之相连的 另一个点染的色,居然与之相同,则染色失败 */ #include<bits/stdc++.h> using namespace std; struct Node { int x,y,z; }e[200020]; int tot,s,n,m; int head[20005],b[20005],a[100005]; bool f; int cmp(int a,int b){return a<b;} void insert(int x,int y,int z) { e[++tot].x=y; e[tot].y=z; e[tot].z=head[x]; head[x]=tot; } void dfs(int x,int data) { if(f==false) //如果已染色失败,则退出 return; b[x]=data;//将x这个点染成data色 for(int i=head[x];i!=0;i=e[i].z) //遍历所有与x相连的边 { if(e[i].y>s) //注意这一句,并不是所有的边都要走的 //这也是前面为什么要记下每条边冲突值的原因 { if(b[e[i].x]==-1) //如果这个点没有被染色 dfs(e[i].x,1-data); //染成另一种颜色 else if(b[e[i].x]==data) //如果已被染了,且与当前点一样,则染色失败 { f=false; return; } } } } bool work(int midd) { memset(b,-1,sizeof(b)); f=true;//假设能染色成功 s=a[midd];//记下第midd条冲突的怨气值 for(int i=1;i<=n;i++) if(b[i]==-1) //注意前midd条规则所产生的,可能是一个森林, //于是当发现一个点没有被染色,就对它进行染色 dfs(i,0); return f; } int main() { cin>>n>>m; memset(head,0,sizeof(head)); int x,y,z; tot=0; for(int i=1;i<=m;i++) { scanf("%d%d%d",&x,&y,&z); insert(x,y,z); insert(y,x,z); a[i+1]=z; //记下这条边的冲突值,在后面有用到的 } a[1]=0; //有可能所有冲突都可以避免,所以要有边权的0的。 sort(a,a+m+1,cmp);//对冲突值进行升序排列 int l=1,r=m+1,mid; while(l<r) //二分答案,二分一个冲突编号出来,以它的冲突值为最小值,将值大于它的边进行构图。 { mid=(l+r)/2; if(work(mid)==true) //染色成功,说明冲突值还可以于变小一点,于是右边界移动。 r=mid; else l=mid+1; } cout<<a[l]; }