题目详情
甲乙两个人用一个英语单词玩游戏。两个人轮流进行,每个人每次从中删掉任意一个字母,如果剩余的字母序列是严格单调递增的(按字典序a < b < c <....<z),则这个人胜利。两个人都足够聪明,甲先开始,问他能赢么?
输入: 一连串英文小写字母,长度不超过15,保证最开始的状态不是一个严格单增的序列。
输出:1表示甲可以赢,0表示甲不能赢。
例如: 输入 bad, 则甲可以删掉b或者a,剩余的是ad或者bd,他就赢了,输出1。
又如: 输入 aaa, 则甲只能删掉1个a,乙删掉一个a,剩余1个a,乙获胜,输出0。
此题属于明显的博弈问题,但是比较遗憾的是在我的acm生涯中没接触过博弈论这个部分,因此刚开始尝试利用模拟的方法解决,利用极大极小搜索+alpha beta剪枝找到最优解,实现后提交结果超时了,继续优化后没有发现比较好的剪枝方法,因此不得不换个思路。
百度了下博弈论的基本概念,顿时茅塞顿开,解决这个问题,只需要知道两个基本概念即可!
1.一个局势只能是必胜局势或必败局势两种
2.如果一个局势能通过操作变为必败局势,那么这个局势就是必胜的,否则是必败的
此题即求初始的序列是否是必胜局势,不难发现此题的必败局势是升序序列,因为在到达这个局势时对手已经赢了,我们可以枚举当前局势的每种子局势,判断它是否是必胜还是必败,然后得到当前局势的状态。
static const int fastbit[32] = {
0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
};
#define FASTBIT(X) (fastbit[((unsigned int)(((X)&-(X))*0x077CB531U))>>27])
int state[(1<<16)-1]; //0代表必败,1代表必胜
int who(char *word)
{
int len;
int i ,j ,t ,k;
len = strlen(word);
memset(state, 0, sizeof(state));
for(i = 1 ;i < (1<<len) ;i ++) {
t = i;
k = -1;
while(t) {
j = FASTBIT(t);
if(k >= 0 && word[k] >= word[j]) { //判断是否升序
break;
}
k = j;
t &= ~(t&-t);
}
if(!t) {
continue;
}
t = i;
k = 0;
while(t && !state[i]) { //枚举当前局势的子局势
j = FASTBIT(t);
state[i] = state[(t&~(1<<j))|k]^1;
k = ((1<<(j+1))-1)&i;
t &= ~(t&-t);
}
}
return state[(1<<len)-1];
}