题目描述 Description
某个地区有n(n<=1000)个犯罪团伙,当地警方按照他们的危险程度由高到低给他们编号为1-n,他们有些团伙之间有直接联系,但是任意两个团伙都可以通过直接或间接的方式联系,这样这里就形成了一个庞大的犯罪集团,犯罪集团的危险程度唯一由集团内的犯罪团伙数量确定,而与单个犯罪团伙的危险程度无关(该犯罪集团的危险程度为n)。现在当地警方希望花尽量少的时间(即打击掉尽量少的团伙),使得庞大的犯罪集团分离成若干个较小的集团,并且他们中最大的一个的危险程度不超过n/2。为达到最好的效果,他们将按顺序打击掉编号1到k的犯罪团伙,请编程求出k的最小值。
输入描述 Input Description
第一行一个正整数n。接下来的n行每行有若干个正整数,第一个整数表示该行除第一个外还有多少个整数,若第i行存在正整数k,表示i,k两个团伙可以直接联系。
输出描述 Output Description
一个正整数,为k的最小值
样例输入 Sample Input
7
2 2 5
3 1 3 4
2 2 4
2 2 3
3 1 6 7
2 5 7
2 5 6
样例输出 Sample Output
1
数据范围及提示 Data Size & Hint
n<=1000
输出1(打击掉红色团伙)
解题思路
这题的题意就是正向顺序删点1~K,使得每个集合的个数都不超过(n + 1) / 2,问最小的K是多少?
正向顺序删点?感觉跟之前的 Hdu 4496 D-City 有点相像;
如果正向顺序删点的话,那么每一次都需要重新维护并查集,所以我们可以逆向考虑,即从n~1开始枚举,将点加入图中,此时意味着删除了1 ~ K - 1,图还剩K ~ n;
若此时剩下每个集合的个数还不超过(n + 1)/ 2,则继续逆向枚举,则到超过为止;
代码如下
1 #include<iostream> 2 using namespace std; 3 const int N = 1010; 4 int f[N], a[N][N], sum[N], n; 5 void init(){ 6 for(int i = 1; i <= n; i++){ 7 f[i] = i; 8 sum[i] = 1; 9 } 10 } 11 int getf(int x){ 12 if(x == f[x]) return x; 13 else return f[x] = getf(f[x]); 14 } 15 int main(){ 16 cin >> n; 17 init(); 18 for(int i = 1; i <= n; i++){ 19 cin >> a[i][0]; 20 for(int j = 1; j <= a[i][0]; j++){ 21 cin >> a[i][j]; 22 } 23 } 24 for(int i = n; i >= 1; i--){ //逆向枚举 25 for(int j = 1; j <= a[i][0]; j++){ 26 if(a[i][j] > i){ //因为是顺序删除,所以删除该点时,那么之前的点已经删除了 27 int t1 = getf(i), t2 = getf(a[i][j]); 28 if(t1 != t2){ 29 f[t2] = t1; 30 sum[t1] += sum[t2]; 31 if(sum[t1] > (n + 1) / 2){ 32 cout << i << endl; 33 return 0; 34 } 35 } 36 } 37 } 38 } 39 return 0; 40 }
打击犯罪
原文地址:https://www.cnblogs.com/zoom1109/p/11052024.html
时间: 2024-10-07 16:09:41