#并查集#SSL 2342 信息学奥赛一本通 1386 打击犯罪

本文介绍了一种解决信息学奥赛问题的算法:如何通过逆向枚举找到需要删除的点,使得剩下的团伙规模不超过总数的一半。文章提供了两种实现方式:邻接矩阵和邻接表。

信息学奥赛一本通链接


题目

求最少打击的团伙数,并且使最大的团伙危险程度不超过n/2。


分析

n n n 1 1 1逆向枚举,每次把点 k k k加入图中,也就是删掉 1 ∼ k − 1 1\sim k-1 1k1,剩余 k ∼ n k\sim n kn,若最大集合点数不超过 n / 2 n/2 n/2,说明这种方案可行, k k k还能更小,一旦不满足,意味着第 k k k个点加入图中就不可行了,所以点 k k k必须得删,所以输出答案。


邻接矩阵代码(0ms 5152k)

#include <cstdio>
#include <cctype>
using namespace std;
int n,a[1001][1001],c[1001],f[1001];
int in(){
	int ans=0; char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans;
}
int getf(int u){return (f[u]==u)?u:f[u]=getf(f[u]);}
int main(){
	n=in();
	for (int i=1;i<=n;i++){
		c[i]=1; f[i]=i; a[i][0]=in();
		for (int j=1;j<=a[i][0];j++)
		a[i][j]=in();
	}
	for (int k=n;k>=1;k--)
	for (int i=1;i<=a[k][0];i++)
	if (a[k][i]>k){//加入
		int x=getf(k),y=getf(a[k][i]);
		if (x!=y){//不同集合
			f[y]=x;
			c[x]+=c[y];//团伙
			if (c[x]>n/2){//超过
				printf("%d",k);
				return 0;
			}
		}
	}
}

邻接表代码(0ms 1276k)

#include <cstdio>
#include <cctype>
using namespace std;
struct node{int y,next;}e[5001];//不可能太多的
int n,m,ls[1001],c[1001],f[1001];
int in(){
	int ans=0; char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans;
}
int getf(int u){return (f[u]==u)?u:f[u]=getf(f[u]);}
int main(){
	n=in(); int t,x,y;
	for (int i=1;i<=n;i++){
		c[i]=1; f[i]=i; t=in();
		for (int j=1;j<=t;j++){//邻接表
		    e[++m].y=in();
			e[m].next=ls[i]; 
			ls[i]=m;
		}
	}
	for (int k=n;k>=1;k--){
	    t=ls[k];
	    while (t){
		if (e[t].y>k){//犯罪团伙
			x=getf(k); y=getf(e[t].y);
			if (x!=y){//不在集合
				f[y]=x;
				c[x]+=c[y];
				if (c[x]>n/2){//超过
					printf("%d",k);
					return 0;
				}
			}
		}
		t=e[t].next;
	    }
	}
}
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值