打击犯罪【并查集】

>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;
}

猜你喜欢

转载自blog.csdn.net/qq_43010386/article/details/91570832