P1525 关押罪犯 - 二分+二分图染色||并查集

传送门

一.二分+二分图染色

最小的最大,考虑二分。

每次二分影响值,将影响值不小于mid的两个人关进不同的监狱,即染成不同的颜色,分到二分图的两个不同的集合中。若最终形成的图是二分图,说明这种分法可行,影响值还有变小的可能;否则影响值只能更大。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=20000+100,M=100000+100;
int n,m;
struct node{
    int to,nxt;
    long long w;
}e[M<<1];
int head[N],tot;
void add(int u,int v,long long w){
    e[++tot]=(node){v,head[u],w};
    head[u]=tot;
}
struct ed{
    int u,v;
    long long w;
}E[M];
int col[N];
bool dfs(int u,int c,long long mid){
    col[u]=c;
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(e[i].w>=mid){
            if(col[v]==col[u]) return false;
            if(!col[v]&&!dfs(v,3-c,mid)) return false;    
        }
    }
    return true;
}
bool check(long long mid){
    for(int i=1;i<=n;i++) col[i]=0;
    //不必重新建图,只保留边权>mid的即可 
    for(int i=1;i<=n;i++) if(!col[i]) if(!dfs(i,1,mid)) return false;
    return true;
}
int main(){
    scanf("%d%d",&n,&m);
    long long l=0,r=-(1<<30);
    for(int i=1;i<=m;i++){
        int u,v;
        long long w;
        scanf("%d%d%lld",&u,&v,&w);
        int c=u;
        u=min(u,v);v=max(c,v);
        add(u,v,w);add(v,u,w);
        E[i]=(ed){u,v,w};
        r=max(r,w);
    }
    r++;//扩大边界,防止漏解 
    while(l+1<r){
        long long mid=(l+r)>>1;
        if(check(mid)) r=mid;
        else l=mid;
    }
    printf("%lld",l);
    return 0;
}

二.并查集

贪心地想,将每对罪犯按影响值从大到小进行排序,每次将两个罪犯分到两个监狱中,直到出现冲突的情况,输出答案。

我们可以通过并查集来维护两个集合并查询集合所属关系。对于每个罪犯i,集合fa[i]表示在监狱1,集合fa[i+n]表示在监狱2。若出现两个罪犯在同一监狱的情况,说明出现了冲突,此时这对罪犯的影响值就是最小的影响值即为答案。最后注意特判没有冲突的情况。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=20000+100,M=100000+100;
struct node{
    int u,v;
    long long w;
}e[M];
bool cmp(node a,node b){
    return a.w>b.w;
}
int fa[N<<1];
int find(int x){
    return fa[x]==x?x:fa[x]=find(fa[x]);
}
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        int u,v;
        long long w;
        scanf("%d%d%lld",&u,&v,&w);
        e[i]=(node){u,v,w};
    }
    sort(e+1,e+m+1,cmp);
    for(int i=1;i<=(n<<1);i++) fa[i]=i;
    for(int i=1;i<=m;i++){
        int u=e[i].u,v=e[i].v;
        int fu=find(u),fv=find(v);
        if(fu==fv){
            printf("%lld",e[i].w);
            return 0;
        }
        else {
            fa[fu]=find(v+n);
            fa[fv]=find(u+n);
        }
    }
    printf("0");
    return 0;
}

 

转载于:https://www.cnblogs.com/Loi-Brilliant/p/9124315.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值