51nod1490多重游戏(博弈 SG函数+找可败点)

题目链接
题解:可以想到先建一棵trie树,然后每次转移就是在一个dag上转移。就自然的想到sg函数。但是此题目玩多次游戏,且输的一方要换先手。且最后一局才分胜负。我们可以从后往前,从最后一局游戏的胜负考虑。
分析:若sg = 0,则此时最后一局后手必胜。想要获得后手,则上一局游戏必胜。又因为上一局后手必胜,由此推到,第一局游戏的后手将获得最后胜利。
若sg = 1,则此时相当于每次取一个石子。别无选择。所以我们判奇偶性
若sg >1,则最后我们要做先手,我们上一局要输。但是sg函数是用来判断必胜点的。意思就是假设我们当前sg = 0,不意味着后手就傻傻的要我们输,因为可能sg=0可以往sg>0上转移。所以后手也可以让我们赢。所以我们要判断可败点。
则有:
叶子是可败点
若一个点是可败点,那么其后继状态有一个只能胜点
若一个点是只能胜点,其后继状态必然都是可败点

#include
#define ll long long 
using namespace std;
const int maxn = 1e5+5;
const int sigma_size = 26;
int tot=1;int ch[maxn][26];
void insert(char *s){
	int len = strlen(s);
	int u = 0;
	for(int i=0;i<len;i++){
		int c = s[i]-'a';
		if(!ch[u][c]){
			ch[u][c]=tot++;
		}
		u = ch[u][c];
	}
} 
int dfs(int u){
	int vis[35];memset(vis,0,sizeof(vis));
	for(int i=0;i<26;i++){
		if(ch[u][i])vis[dfs(ch[u][i])]=1;
	}
	for(int i=0;;i++){
		if(!vis[i]){
			return i;
		}
	}
} 
int n,k;
char s[maxn]; 
bool judge(int u){
	int flag=0;
	for(int i=0;i<26;i++){
		if(ch[u][i]){
			if(judge(ch[u][i])){
				return 0;
			} 
			flag=1;
		}
	}
	if(!flag)return 0;//叶子可败 
	else return 1;//如果有一个只能胜点,则当前点为可败点 
}
int main(){
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++){
		scanf("%s",s);
		insert(s);
	}
	int val = dfs(0);
	if(!val)puts("Second");
	else if(val==1){
		if(k&1)puts("First");
		else puts("Second");
	}else{
		if(!judge(0))puts("First");//可以必败 
		else{
			if(k&1)puts("First");
			else puts("Second");
		}
	}
	return 0;
} 

你可能感兴趣的:(博弈)