codevs 5971 打擊犯罪

題目描述 Description

    某個地區有n(n<=1000)個犯罪團伙,當地警方按照他們的危險程度由高到低給他們編號爲1-n,他們有些團伙之間有直接聯繫,可是任意兩個團伙均可以經過直接或間接的方式聯繫,這樣這裏就造成了一個龐大的犯罪集團,犯罪集團的危險程度惟一由集團內的犯罪團伙數量肯定,而與單個犯罪團伙的危險程度無關(該犯罪集團的危險程度爲n)。如今當地警方但願花盡可能少的時間(即打擊掉儘可能少的團伙),使得龐大的犯罪集團分離成若干個較小的集團,而且他們中最大的一個的危險程度不超過n/2。爲達到最好的效果,他們將按順序打擊掉編號1到k的犯罪團伙,請編程求出k的最小值。ios

輸入描述 Input Description

   第一行一個正整數n。接下來的n行每行有若干個正整數,第一個整數表示該行除第一個外還有多少個整數,若第i行存在正整數k,表示i,k兩個團伙能夠直接聯繫。編程

輸出描述 Output Description

    一個正整數,爲k的最小值ide

樣例輸入 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

1spa

數據範圍及提示 Data Size & Hint

n<=1000

輸出1(打擊掉紅色團伙)code

解題思路

  這題的題意就是正向順序刪點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 }
打擊犯罪
相關文章
相關標籤/搜索