并查集基础 真的很基础
题目描述:Description
某个地区有n(n<=1000)个犯罪团伙,当地警方按照他们的危险程度由高到低给他们编号为1-n,他们有些团伙之间有直接联系,但是任意两个团伙都可以通过直接或间接的方式联系,这样这里就形成了一个庞大的犯罪集团,犯罪集团的危险程度唯一由集团内的犯罪团伙数量确定,而与单个犯罪团伙的危险程度无关(该犯罪集团的危险程度为n)。现在当地警方希望花尽量少的时间(即打击掉尽量少的团伙),使得庞大的犯罪集团分离成若干个较小的集团,并且他们中最大的一个的危险程度不超过n/2。为达到最好的效果,他们将按顺序打击掉编号1到k的犯罪团伙,请编程求出k的最小值。
Input Format
第一行一个正整数n。接下来的n行每行有若干个正整数,第一个整数表示该行除第一个外还有多少个整数,若第i行存在正整数k,表示i,k两个团伙可以直接联系。
Output Format
一个正整数,为k的最小值
Sample
样例输入
7
2 2 5
3 1 3 4
2 2 4
2 2 3
3 1 6 7
2 5 7
2 5 6
样例输出
1
题目分析:
由题可知,我们需要打击1~k个犯罪团伙,来让其最大的总数不超过n/2(n<<1)。
1.如果我们正向遍历并查集的话,就需要在并查集内删点,这个操作对并查集并不是很友好。所以可以考虑另外一种方法。
2.既然要打击1~k的罪犯,我们不如从第n个团伙开始建立集合,每次加边时判断一下是否满足(<=n/2),直到不能满足题意为止。
AC code:
#include<bits/stdc++.h>
using namespace std;
#define s(n) scanf("%d",&n)
int n,m,a[1010][1010],z,ans,dgr[1010]={1},lst=0,mx,f[1010];
bool fg[1010]={0};
inline bool check(){
int ch = 0;
for(int z=1; z<=n; z++){
ch = max(dgr[z],ch);
}
if(ch <= n/2) return true;
return false;
}
inline int ff(int k){
if(f[k] == k) return k;
else return ff(f[k]);
}
int main(){
for(int i=0; i<1010; i++) dgr[i] = 1, f[i] = i;
s(n);
for(int i=1; i<=n; i++){
s(m);
fg[i] = 1;
int cnt = 1;
for(int j=1; j<=m; j++){
s(z);
a[i][cnt++] = z;
}
}
for(int i=n; i>=1; i--){
for(int j=1; a[i][j]; j++){
if(a[i][j] > i){
int fi = ff(i), fa = ff(a[i][j]);
if(fi != fa){
dgr[fi] += dgr[fa];
f[fa] = fi;
if(!check()){
printf("%d",i);
return 0;
}
}
}
}
}
return 0;
}
标签:打击,正整数,int,查集,fa,罪犯,fi,1010
From: https://www.cnblogs.com/xiaolemc/p/17936449.html