字符串游戏-博弈论-trie

Description

  从前有个游戏。游戏分为 k 轮。

  给定一个由小写英文字母组成的字符串的集合 S,

  在每轮游戏开始时,双方会得到一个空的字符串,

  然后两人轮流在该串的末尾添加字符,并且需要保证新的字符串是 S 中某个串的前缀,直到有一方不能操作,则不能操作的一方输掉这一轮。

  新的一轮由上一轮输的人先手,最后一轮赢的人获得游戏胜利。

  假定双方都采取最优策略,求第一轮先手的一方能否获胜。

Input

  输入包含多组数据。

  每组数据的第一行包含两个整数 n,k,分别表示字符串的数量和游戏的轮数。

  接下来 n 行,每行一个由小写英文字母组成的字符串。

Output

  对于每组数据输出一行,若先手能获胜输出 HY wins!,否则输出 Teacher wins!

Sample Input

  2 3
  a
  b
  3 1
  a
  b
  c

Sample Output

  HY wins!
  HY wins!

HINT

  1 ≤ n ≤ 1e5,1 ≤ k ≤ 1e9,保证所有字符串长度不超过 1e5,数据组数不超过 10。


思路

  • 实现:f数组:两人都想赢时是否有必赢策略,g数组:两人都想输时是否有必输策略
  • 先手无必胜策略,则先手在k轮游戏中都是先手,必败;
  • 先手有必胜策略,无必败策略,则每轮游戏双方都会交换先后手,如果进行了奇数轮游戏,则先手必胜,否则先手必败;
  • 先手既有必胜策略也有必败策略,则先手可以在前k-1轮游戏中使用必败策略,在最后一轮使用必胜策略,因此先手必胜。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int n,k,cnt,f[1000005],g[1000005];
char s[100005];
struct fdfdfd{
	int ch[27],flag;
	void init(){memset(ch,0,sizeof(ch)); flag=0;}
}e[1000005];
void insert(char a[],int len)
{
	int root=1;
	for(int i=0;i<len;++i)
	{
		int j=a[i]-'a'+1; e[root].flag=1;
		if(!e[root].ch[j]) {e[root].ch[j]=++cnt; e[cnt].init();}
		root=e[root].ch[j];
	}
}
void dfs(int x)
{
	if(!e[x].flag) {g[x]=1; f[x]=0; return;}
	f[x]=0; g[x]=0;
	for(int i=1;i<=26;++i)
	{
		if(!e[x].ch[i]) continue;
		dfs(e[x].ch[i]);
		if(!f[e[x].ch[i]]) f[x]=1;
		if(!g[e[x].ch[i]]) g[x]=1;
	}
}
int main()
{
	while(scanf("%d%d",&n,&k)!=EOF)
	{
		cnt=1; e[1].init(); memset(f,0,sizeof(f)); memset(g,0,sizeof(g));
		for(int i=1;i<=n;++i) scanf("%s",&s),insert(s,strlen(s));
		dfs(1);
		if((f[1]&&g[1])||(f[1]&&(k%2==1))) puts("HY wins!");
		else puts("Teacher wins!");
	}
	return 0;
}
posted @ 2020-10-06 14:39  wuwendongxi  阅读(315)  评论(0编辑  收藏  举报