洛谷 P1525 关押罪犯 NOIp2010提高组 (贪心+并查集)
题目链接:https://www.luogu.org/problemnew/show/P1525
题目分析
通过分析,我们可以知道,这道题的抽象意义就是把一个带边权的无向图,分成两个点集,使得两个集合中的每两个点之间的边权最大的最小。问这个边权是多少。
我们不妨可以想一下,如果$a$和$b$是敌人,但$a$和$b$的有些敌人不是敌人,对于处理$a$来说,肯定是把他和那些$b$的敌人放在一起比较好。 再来看下题目,尽可能地让最大的最小,所以我们可以贪心一下下~~~ 把所有的“敌人对”按照边权从大到小排个序。 然后按顺序判$a$ ,$b$在不在一个点集里头(这里便用到了并查集,直接查父亲是不是一个就好),如果在,就可以输出边权了。如果不在,把$a$和$b$的敌人放在一起,$b$和$a$的敌人放在一起(参见上上行)。
肯定有人要问为什么。呃。。 因为边权是排过序的,第一个匹配不上的肯定是最优解。(此处需脑补一下。。)
呃。。然后就可以开始码代码了。
#include <stdio.h> #include <algorithm> using namespace std; const int M=100003; const int N=20003; struct node { int x, y, z; }f[M]; int a[N]; int b[N]; bool cmp(node x,node y) { return x.z > y.z; } int find(int x) { if(a[x] == x) return x; a[x] = find(a[x]); return a[x]; } void update(int x,int y) { int fx = find(x); int fy = find(y); a[fx] = fy; } bool check(int x,int y) { int fx = find(x); int fy = find(y); if(fx == fy) return true; return false; } int main() { int n, m; scanf("%d%d",&n,&m); for(int i = 1;i <= n; i ++) a[i]=i; for(int i = 1;i <= m; i ++) scanf("%d%d%d",&f[i].x,&f[i].y,&f[i].z); sort(f+1,f+1+m,cmp); for(int i = 1;i <= m+1; i ++) { if(check(f[i].x,f[i].y)) { printf("%d",f[i].z); break; } else { if(!b[f[i].x]) b[f[i].x]=f[i].y; else update(b[f[i].x],f[i].y); if(!b[f[i].y]) b[f[i].y]=f[i].x; else update(b[f[i].y],f[i].x); } } return 0; }