HDU_4155
首先容我吐槽一下:说好的0个数字呢……
这个题一开始用gets读的,WA得我死去活来的,后来找了个AC的程序发现它不处理空行,于是我改成了scanf试了一下,AC了……
这个题符合公平组合游戏的特征,所以可以用SG函数理论去解,只不过面临两个问题:第一,如何抽象成有向无环图上的游戏,其实主要也就是说每个节点应该如何描述的问题,第二,终止状态是什么。
首先,第二个问题想了一下之后解决了,尽管我们不能直接认定哪些状态是终止状态,但我们如果搜到了一个点并且发现这个点无法进行决策,那么自然就可以认为是终止状态,对应的SG函数的值也自然是0。
在想第二个问题的时候,我们就会想到,如果对当前一个状态进行决策的话,必然要知道还有哪些牌可以用,最多的时候就是6种牌都可以用,但是如果某些牌的张数已经达到了4张,那么这种牌自然就不能用了,于是我们就发现每个节点必须要保留当前每种牌还剩多少,或者说用了多少,要不我就没有决策的依据了。
想到这,再看一下牌种和牌数又不多,所以我们就可以把牌的状态用5进制压缩成一个整数了,而且还有一个好处就是我们可以直接把这个整数表示有向无环图上的节点,这样第一个问题也迎刃而解了。同时,由于这个题目SG函数的值只和当前局面的状态有关系,所以所有的case可以共用一个sg数组,这样能还可以节省一些时间。
另外推荐一个讲SG函数入门知识的博客:http://www.cnblogs.com/Knuth/archive/2009/09/05/1561007.html。
#include<stdio.h>
#include<string.h>
#define MAXD 16000
int sg[MAXD], h[7];
char b[30];
int dfs(int st)
{
if(sg[st] != -1)
return sg[st];
int i, j, k, s[10], sum = 0;
memset(s, 0, sizeof(s));
for(i = 1; i <= 6; i ++)
sum += h[i] * i;
for(i = 1; i <= 6; i ++)
if(h[i] < 4 && i + sum <= 31)
{
++ h[i];
for(j = 1, k = 0; j <= 6; j ++)
k = k * 5 + h[j];
s[dfs(k)] = 1;
-- h[i];
}
for(i = 0; s[i]; i ++);
return sg[st] = i;
}
void solve()
{
int i, j, k, turn;
memset(h, 0, sizeof(h));
for(i = 0; b[i]; i ++)
++ h[b[i] - '0'];
turn = i % 2;
for(i = 1, j = 0; i <= 6; i ++)
j = j * 5 + h[i];
k = dfs(j);
printf("%s ", b);
if(turn == 0)
printf("%s\n", k == 0 ? "B" : "A");
else
printf("%s\n", k == 0 ? "A" : "B");
}
int main()
{
memset(sg, -1, sizeof(sg));
while(scanf("%s", b) == 1)
{
solve();
}
return 0;
}