题目传送门:https://www.luogu.org/problemnew/show/P1525
题意:
有两个监狱,n个罪犯,有m组罪犯之间有怒气,求让所有罪犯都关进监狱的最小的怒气值(最小怒气值为两个监狱的最大怒气值)。注意:有两个罪犯x,y,有且仅有x,y在同一个监狱时才有怒气值。
思路:
从数据范围和题意可以看出并查集。
并查集类型的题目需要记住:朋友的敌人是我的敌人,敌人的敌人是我的朋友。
那么我们可以贪心地想到:越大的怒气值的一组罪犯应分在两个不同的监狱里。
解法:
想到这一点,我们就可以知道可以进行排序(关键字为怒气值),将两个罪犯分在不同的监狱,即使他们的祖先不同。
那么,我们就知道,如果当前的两个罪犯的祖先相同(他们必须在同一监狱),那么他们的怒气值即为所求(因为怒气值从大到小排序,这是当前在同一监狱的最大怒气值)。
否则呢?对于每一个罪犯,他所对的另一个罪犯就为他的敌人;如果他已经有敌人了,那么他的敌人就和另一个罪犯为朋友关系,在同一个监狱(因为我的敌人和我的另一个敌人为朋友),将他们合并即可。
代码:
#include<cstdio>
#include<algorithm>
using namespace std;
struct node{int x,y,z;} a[200000];
int n,m;
int fa[30000],foe[30000];//fa[i]表示i的祖先,foe[i]表示i的敌人
bool cmp(node x,node y)
{
return x.z>y.z;
}
int find(int x)//并查集路径优化,寻找祖先
{
return x==fa[x]?x:fa[x]=find(fa[x]);
}
void work(int x,int y)//合并
{
fa[find(x)]=find(y);
}
int main()
{
int x,y,z;
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++)
scanf("%d %d %d",&a[i].x,&a[i].y,&a[i].z);
for(int i=1;i<=n;i++)
fa[i]=i;
sort(a+1,a+m+1,cmp);
for(int i=1;i<=m;i++)
{
int x=a[i].x,y=a[i].y,t1=find(x),t2=find(y);
if(t1==t2)
{
printf("%d",a[i].z);
return 0;
}
if(!foe[x]) foe[x]=y; else work(foe[x],y);
if(!foe[y]) foe[y]=x; else work(foe[y],x);
}
printf("0");
}