P1525 关押罪犯·并查集/二分+二分图

题解

版本1:并查集 - 俗话说敌人的敌人就是朋友
遍历所有的冲突,如果冲突双方不在同一个监狱里(不在同一个监狱的条件:其集合祖先fa[i]不是同一个),将一方归入另一方敌人所在的监狱,利用并查集,将同一监狱的人合并,
至于怎么遍历,贪啊!

版本2:二分+二分图
最小的最大冲突影响力 - - 首先想到二分单调答案
两个监狱 - - 二分图,判断是否符合条件,为了防止MLE,二分图采用bfs形式


P1525 关押罪犯·并查集/二分+二分图_第1张图片P1525 关押罪犯·并查集/二分+二分图_第2张图片


并查集

#include 
using namespace std;
const int N=1e6+10;
int n,m;
struct edge{
    int u,v,w;
    void init(){
        cin>>u>>v>>w;
    }
}e[N];
bool cmp(edge x,edge y){return x.w>y.w;}
int fa[N];//将同盟者归为一类
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
bool judge(int u,int v){return find(u)==find(v);}
void join(int u,int v){
    u=find(u);v=find(v);
    fa[u]=v;
}
int enemy[N];//敌人
int main(){
    cin>>n>>m;
    for (int i = 1; i <= n; ++i) fa[i]=i;
    for (int i = 1; i <= m; ++i) e[i].init();
    sort(e+1,e+1+m,cmp);//从大到小排列 贪心
    
    for (int i = 1; i <= m; ++i) {
        int u=e[i].u;
        int v=e[i].v;
        if(judge(u,v)){//如果在同一个集合内
            return printf("%d", e[i].w),0;
        }else{
            if(!enemy[u]) enemy[u]=v;
            else join(enemy[u],v);//合并敌人

            if(!enemy[v]) enemy[v]=u;
            else join(enemy[v],u);
        }
    }
    puts("0");//当没有冲突时 输出0
    return 0;
}

二分+二分图(bfs)

#include 
using namespace std;
const int N=1e6+10;
int n,m;
struct edge{
    int next,to,w;
}e[N];
int head[N],tot=0;
int color[N];
void add(int u,int v,int w){
    e[++tot].to=v;
    e[tot].w=w;
    e[tot].next=head[u];
    head[u]=tot;
}
bool check(int ans){
    queue<int>q;
    memset(color, 0, sizeof(color));//每次判断都要清空
    for (int i = 1; i <= n; ++i) {
        if(!color[i]){
            q.push(i);
            color[i]=1;
            while(q.size()){
                int u=q.front();
                q.pop();
                for (int j = head[u]; j; j=e[j].next) {
                    if(e[j].w>ans){//冲突影响力值大于答案 说明要分图
                        int v=e[j].to;
                        if(!color[v]){//如果没有染过色
                            color[v]=3-color[u];
                            q.push(v);
                        }else if(color[v]==color[u])return false;
                    }
                }
            }
        }
    }
    return true;
}
int main(){

    int r=0;
    cin>>n>>m;
    for (int i = 1,u,v,w; i <= m; ++i) {
        cin>>u>>v>>w;
        add(u,v,w);
        add(v,u,w);
        r=max(r,w);//二分边界
    }
    
    int l=0,mid;
    while(l<r){
        mid=(l+r)/2;
        if(check(mid)){
            r=mid;
        }else l=mid+1;
    }
    printf("%d",r);
    return 0;
}

你可能感兴趣的:(洛谷)