今天刷到一道挺有意思的题目关押罪犯
学到了种类并查集跟染色二分图,记录一下
二分+染色二分图
#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();
}