【题解】打击犯罪

题目描述

        某个地区有n(n≤1000)个犯罪团伙,当地警方按照他们的危险程度由高到低给他们编号为1至n,他们有些团伙之间有直接联系,,但是任意两个团伙都可以通过直接或间接的方式联系,这样这里就形成了一个庞大的犯罪集团,犯罪集团的危险程度由集团内的犯罪团伙数量唯一确定,而与单个犯罪团伙的危险程度无关(该犯罪集团的危险程度为n)。现在当地 警方希望花尽量少的时间(即打击掉尽量少的团伙),使得庞大的犯罪集团分离成若干个较小的集团,并且他们中最大的一个的危险程度不超过 n/2。为达到最好的效果,他们将按顺序打击掉编号1到k的犯罪团伙,请编程求出k的最小值。

 

输入格式

        第一行,一个正整数 n。

        接下来的n行,每行有若干个正整数,第一个整数表示该行除第一个外还有多少个整数,若第i行存在正整数k,表示i,k两个团伙可以直接联系。

 

输出格式

        一个正整数,为k的最小值。

 

输入样例

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(打击犯罪团伙)

        Failed to load picture

题解

        我们可以倒序枚举,从$n$枚举到$1$。

        当我们枚举到$i$的时候,我们就合并满足$i < j$的边$(i, j)$,合并后判断当前$i$所在集合的点数是否大于$\frac{n}{2}$即可。

#include <iostream>
#include <cstdio>

#define MAX_N (1000 + 5)

using namespace std;

int n;
int a[MAX_N][MAX_N], l[MAX_N];
int r[MAX_N], c[MAX_N];

int Root(int x)
{
    int R = x, tmp;
    while(R != r[R]) R = r[R];
    while(x != r[x]) tmp = r[x], r[x] = R, x = tmp;
    return R;
}

void Merge(int x, int y)
{
    x = Root(x); 
    y = Root(y);
    r[x] = y;
    c[y] += c[x];
    c[x] = 0;
    return;
}

int main()
{
    scanf("%d", &n);
    for(register int i = 1; i <= n; ++i)
    {
        scanf("%d", l + i);
        for(register int j = 1; j <= l[i]; ++j)
        {
            scanf("%d", a[i] + j);
        }
    }
    for(register int i = 1; i <= n; ++i)
    {
        r[i] = i;
        c[i] = 1;
    }
    for(register int i = n; i; --i)
    {
        for(register int j = 1; j <= l[i]; ++j)
        {
            if(i > a[i][j]) continue;
            if(Root(i) != Root(a[i][j])) Merge(i, a[i][j]);
            if(c[Root(i)] > (n >> 1)) return printf("%d", i), 0;
        }
    }
    return 0;
}
参考程序

猜你喜欢

转载自www.cnblogs.com/kcn999/p/10990524.html