码迷,mamicode.com
首页 > 其他好文 > 详细

P1525 关押罪犯(二分图/并查集)

时间:2020-02-09 18:51:32      阅读:91      评论:0      收藏:0      [点我收藏+]

标签:lse   div   方法   sizeof   pop   排序   ++   span   find   

题意:

给你m对矛盾关系,每对关系分别涉及到x,y两人,矛盾值为w

请你判断分配x和y到两个集合中,能否避免冲突

如能避免请输出0,如果冲突不可避免,请输出最小的矛盾值

思路:

方法①:并查集

并查集能维护连通性、传递性,通俗地说,亲戚的亲戚是亲戚

我们不妨这样想:两个人a,b有仇,那么把他们放在一起显然会打起来,那么我们还不如把a与b的其他敌人放在一起,

因为这样可能会出现“敌人的敌人就是朋友”的情况,恰好a与b的其他敌人之间没有矛盾,那么他们就可以放在同一个集合中,反之b对a亦然

我们可以将没对关系按照权值从大到小排序,这样可以保证一旦发生冲突,答案是最小的

对于加入的每段关系,我们先判断他们是否在同一集合内,如果在的话就说明发生冲突,直接输出答案

如果不在一个集合内,我们就将敌人的敌人加入到自己的集合中

 

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
 using namespace std;
 const int maxn1=2e4+10;
 const int maxn2=1e5+10;
 int fa[maxn1],ene[maxn1];
 struct node{
     int u,v,w;
 }edge[maxn2];
 int find(int x){return fa[x]==x?x:(fa[x]=find(fa[x]));}
 int cmp(node a,node b){return a.w>b.w;}
 int main()
 {
     int n,m;
     scanf("%d%d",&n,&m);
     for(int i=1;i<=m;i++) scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
     for(int i=1;i<=n;i++) fa[i]=i;
     memset(ene,0,sizeof(ene));
     sort(edge+1,edge+1+m,cmp);
     for(int i=1;i<=m;i++){
         int u=edge[i].u,v=edge[i].v,w=edge[i].w;
         int f1=find(u),f2=find(v);
         if(f1==f2){
             cout<<w<<endl;
             return 0;
         }
        else{
            if(!ene[u])    ene[u]=v;
            else    fa[find(ene[u])]=find(v);
            if(!ene[v])    ene[v]=u;
            else    fa[find(ene[v])]=find(u);
        }
     }
    cout<<0<<endl;
    return 0;
 }

 

方法②:二分图

看到将犯人分成两批应该很容易想到二分图的做法

那么很明显,不是所有所有情况下都能讲犯人分成两部分,必定有一些冲突是无法避免的

我们也可以注意到,冲突是具有单调性的,我们就可以想到二分的做法

我们二分答案,设当前二分的值为mid,此时任意两个矛盾双方x和y必须被分在两个不同集合中,将罪犯们作为节点,在矛盾值大于等于mid的罪犯之间连一条边,我们得到一张无向图。此时我们只需判定这张无向图是否为二分图即可(因为要分为两部分),如果是二分图,令二分右端点R=mid,否则令L=mid即可

 

#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<cstdio>
 using namespace std;
 const int maxn=2e5+10;
 struct edge{
     int u,v,w;
 }p[maxn];
 int n,m,L=0,R=0,cnt=0,head[maxn];
 void add_edge(int x,int y,int w)
 {
     p[++cnt].u=head[x];
     head[x]=cnt;
     p[cnt].v=y;
     p[cnt].w=w;
 }
 bool judge(int mid)
 {
     queue<int> q;
     int color[maxn]={0};
     for(int i=1;i<=n;i++){
         if(!color[i]) color[i]=1,q.push(i);
         while(!q.empty()){
             int x=q.front();
             q.pop();
             for(int j=head[x];j;j=p[j].u){
                 if(p[j].w>=mid){
                     if(!color[p[j].v]){
                         q.push(p[j].v);
                         if(color[x]==1)    color[p[j].v]=2;
                         else    color[p[j].v]=1;
                     }
                    else if(color[p[j].v]==color[x])
                        return false;
                 }
             }
         }
     }
     return true;
 }
 int main()
 {
     scanf("%d%d",&n,&m);
     for(int i=1;i<=m;i++){
         int u,v,w;
         scanf("%d%d%d",&u,&v,&w);
         R=max(R,w);
         add_edge(u,v,w);
         add_edge(v,u,w);
     }
    int ans=0;
    R++;
    while(L+1<R){
        int mid=(L+R)>>1;
        if(judge(mid))    R=mid;
        else L=mid;
    }
    cout<<L<<endl;
    return 0;
  } 

 

 

 

 

P1525 关押罪犯(二分图/并查集)

标签:lse   div   方法   sizeof   pop   排序   ++   span   find   

原文地址:https://www.cnblogs.com/overrate-wsj/p/12287872.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!