codevs 5971 打击犯罪

题目描述 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 }
打击犯罪

 


 

posted @ 2019-06-19 16:13 zoom1109 阅读(...) 评论(...) 编辑 收藏