题目描述
某个地区有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(打击犯罪团伙)
题解
我们可以倒序枚举,从$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; }
参考程序
原文地址:https://www.cnblogs.com/kcn999/p/10990524.html
时间: 2024-10-01 21:29:17