【随机化贪心好题】部落卫队

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/wljoi/article/details/102689217

众所周知,玄学是万能的

前言

此题正解为dfs,但我希望我的做法可以为您提供一种新的思路。

引入:random_shuffle函数

使用方法:

random_shuffle(a+1,a+n+1);

得到一个元素种类与原数组相同,但顺序被打乱的新数组。

测试如下:

时间复杂度: O ( n ) O(n)

正题

让我们回到本题,题目要求是在 n n 人中选出一些人,使得他们之间没有有仇的人。

看一下数据范围,发现 n 100 n\le 100 ,但dfs+剪枝也是一个玄学的东西,不知道它能否过掉所有的数据点,那么我们就可以祭出我们的玄学随机大法——random_shuffle

做法

我们每次枚举一个选人的顺序,然后从头到尾选,如果能选,我们就选上这个人,并且将其仇敌全部标记,然后更新我们的答案。做一次的准确率太差,经过本人实测,重复次数开在 200000 200000 次左右即可做到既不超时(900+ms),又保证准确率。当然,如果脸白的话,重复次数 1 e 4 1 e 5 1e4-1e5 也是可能过的。

注意

由于本题存在多解但没有SPJ,所以用这种做法还需要判断一下字典序,最后取所有答案中字典序最大的输出。

下面献上我的代码

#include<bits/stdc++.h>
using namespace std;
int first[100005],nxt[100005],to[100005],tot=0;
void Add(int x,int y){
	nxt[++tot]=first[x];
	first[x]=tot;
	to[tot]=y;
}
int n,m,a[105],vis[105],maxn=-1,ans[105];
bool pd(){
	for(int i=1;i<=n;i++){
		if(vis[i]<ans[i])  return 0;
		if(vis[i]>ans[i])  return 1;
	}
}
int main(){
	srand(time(0));
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		a[i]=i;
	}
	for(int i=1;i<=m;i++){
		int x,y;
		cin>>x>>y;
		Add(x,y);
		Add(y,x);
	}
	for(int i=1;i<=200000;i++){
		memset(vis,-1,sizeof(vis));
		random_shuffle(a+1,a+n+1);
		int num=0;
		for(int i=1;i<=n;i++){
			if(vis[a[i]]==-1){
				num++,vis[a[i]]=1;
				for(int e=first[a[i]];e;e=nxt[e]){
					if(vis[to[e]]==-1)  vis[to[e]]=0;
				}
			}
		}
		if(num>maxn){
			maxn=num;
			for(int i=1;i<=n;i++){
				ans[i]=vis[i];
			}
		}
		if(num==maxn){
			if(pd()){
				for(int i=1;i<=n;i++){
					ans[i]=vis[i];
				}
			}
		}
	}
	cout<<maxn<<endl;
	for(int i=1;i<=n;i++){
		cout<<ans[i]<<" ";
	}
}

后记

此算法并非正解,也有一定可能被卡掉,建议大家在正式考试时除非万不得已,尽量不要使用这种做法。

猜你喜欢

转载自blog.csdn.net/wljoi/article/details/102689217