日常学习~

今天刷到一道挺有意思的题目关押罪犯

学到了种类并查集跟染色二分图,记录一下

二分+染色二分图

#include<bits/stdc++.h>

using namespace std;

typedef pair<int,int>PII;
typedef long long ll;
const int N=1e5+10,M=2*N;

int n,m;
int h[N],ne[M],e[M],w[M],idx;
int color[N];

void add(int a,int b,int c)
{
    e[idx]=b;
    ne[idx]=h[a];
    w[idx]=c;
    h[a]=idx++;
}

int dfs(int u,int c,int mid)
{   
    color[u]=c;
    for(int i=h[u];i!=-1;i=ne[i])
    {
        if(w[i]<=mid) continue;//小于等于的可以直接放在同一个监狱
        int j=e[i];
        if(!color[j])
           { if(!dfs(j,3-c,mid)) return 0;}
        else if(color[j]==color[u]) return 0;
    }
    return 1;
}

int check(int mid)
{
    memset(color,0,sizeof color);
    for(int i=1;i<=n;i++)
    {
        if(!color[i])
            if(!dfs(i,1,mid)) return 0;
    }
    return 1;
}

void solve()
{
    memset(h,-1,sizeof h);
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c);add(b,a,c);
    }
    int l=-1,r=1e9+10;
    while(l+1!=r)
    {
        int mid=l+r>>1;
        if(check(mid)) r=mid;
        else l=mid;
    }
    cout<<r<<endl;
}

signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int _=1;
    while(_--)
        solve();
}

种类并查集的做法

开个2倍的区间,对于第i个元素而言,i+n就是他的敌人,也就是要拆开放在不同监狱的仇人关系(不懂的这里可以先去了解一下种类并查集),前半部分区域维护的是朋友的关系,也就是可以放在同一个监狱。

对冲突值进行排序,从大到小进行拆解,如果遇到一对有冲突值并且发现他们已经是朋友关系(即在之前的拆解中被放到了同一个监狱),那么这个冲突值就是答案。如果遍历完没遇到那么说明都可以拆解到两个监狱去,答案为0。

也可以用带权并查集,原理和种类并查集差不多,把用两个集合来维护换成了用边权来维护

关于种类并查集有道经典的题目食物链,需要开3倍的空间进行维护,也可以用带权,原理很相似

#include<bits/stdc++.h>
#define mem(arr,num) memset(arr,num,sizeof arr)

using namespace std;

typedef pair<int,int>PII;
typedef long long ll;
const int N=1e6+10;

struct node{
    int a,b,c;
}p[2*N];
int f[2*N];

bool cmp(node a,node b){return a.c>b.c;}

int find(int u)
{
    if(f[u]!=u) f[u]=find(f[u]);
    return f[u];
}

void solve()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=2*n;i++)  f[i]=i;

    for(int i=1;i<=m;i++)
        cin>>p[i].a>>p[i].b>>p[i].c;
    sort(p+1,p+1+m,cmp);
    
    for(int i=1;i<=m;i++)
    {
        int a=p[i].a,b=p[i].b;
        int x=find(a),y=find(b);
        if(x==y)
        {
            cout<<p[i].c<<endl;
            return;
        }
        f[x]=find(n+b);f[y]=find(n+a);
    }
    cout<<0<<endl;
}

signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int _=1;
    while(_--)
        solve();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值