>Description
某个地区有n(n<=1000)个犯罪团伙,当地警方按照他们的危险程度由高到低给他们编号为1-n,他们有些团伙之间有直接联系,但是任意两个团伙都可以通过直接或间接的方式联系,这样这里就形成了一个庞大的犯罪集团,犯罪集团的危险程度唯一由集团内的犯罪团伙数量确定,而与单个犯罪团伙的危险程度无关(该犯罪集团的危险程度为n)。现在当地警方希望花尽量少的时间(即打击掉尽量少的团伙),使得庞大的犯罪集团分离成若干个较小的集团,并且他们中最大的一个的危险程度不超过n/2。为达到最好的效果,他们将按顺序打击掉编号1到k的犯罪团伙,请编程求出k的最小值。
如下图所示,打击掉1号团伙便能达到目的。
>Input
第一行一个正整数n。
接下来的n行每行有若干个正整数,第一个整数表示该行除第一个外还有多少个整数,若第i行存在正整数k,表示i,k两个团伙可以直接联系。
>Output
一个正整数,为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
>解题思路
用并查集。因为必须“按顺序打击掉编号1到k的犯罪团伙”,所以依次打击掉犯罪团伙,计算每一次的危险程度就可;但是为了节约时间复杂度,就从k到1进行枚举,还是枚举打击的犯罪团伙(i-1),只是这样每次枚举时只用连接一次犯罪团伙的关系(i)了。
>代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,a[1005][1005],c[1005],f[1005],ans,li,sa;
bool lhq;
int ooo(int s)
{
if(c[s]==s) return s;
return c[s]=ooo(c[s]);
} //并查集
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i][0]);
for(int j=1;j<=a[i][0];j++)
scanf("%d",&a[i][j]);
}
for(int i=1;i<=n;i++)
c[i]=i; //c为并查集
for(int i=n;i>=1;i--)
{
lhq=0; //记录是否能达到目的
li=ooo(i);
for(int j=1;j<=a[i][0];j++) //连接之间的关系
if(a[i][j]>=i)
{
sa=ooo(a[i][j]);
if(li<sa) c[sa]=li;
else c[li]=sa;
}
memset(f,0,sizeof(f)); //f记录危险程度
for(int j=i;j<=n;j++)
f[ooo(j)]++;
for(int j=i;j<=n;j++)
if(f[j]>n/2)
{
lhq=1; break;
}
if(!lhq) ans=i-1;
}
printf("%d",ans);
return 0;
}