题目大意:
两个人玩游戏。轮流取数字,范围在1~20之间。
规定:
1. 取过的数字不能再取。
2. 取过的数字的倍数不能再取。
3. 任意个(2)的和不能再取。
当某个人发现没有数字可取时,他就输了。
给出一个“残局”。问,我现在应该怎么取,才能保证胜利。
思路:
其实这个也不是很难的博弈问题。只是搜索就可以解决了。要用位来表示当前剩余数字的状态,
方便保存动态规划的结果。
#include <stdio.h>

#define N 20

char dp[2][1 << (N + 1)];

__inline int use(int visited, int idx)


{
int i;


for (i = 0; i + idx <= N; i++)
{
if (visited & (1 << i))
visited |= 1 << (i + idx);
}
return visited;
}

int dfs(int my_step, int visited)


{
int i, r;

if (visited == (1 << (N + 1)) - 1)
return !my_step;
r = dp[my_step][visited];
if (r)
return r - 1;


for (i = 2; i <= N; i++)
{
if (visited & (1 << i))
continue;
r = dfs(!my_step, use(visited, i));
if (my_step && r)
return 1;
if (!my_step && !r)
return 0;
}

dp[my_step][visited] = !my_step + 1;
return !my_step;
}

int main()


{
int n, v, i, arr[24], cnt, t;

freopen("e:\\test\\in.txt", "r", stdin);


for (t = 1; scanf("%d", &n), n; t++)
{
v = ((1 << (N + 1)) - 1) & ~2;

while (n--)
{
scanf("%d", &i);
v &= ~(1 << i);
}
cnt = 0;

for (i = 2; i <= N; i++)
{
if (v & (1 << i))
continue;
if (dfs(0, use(v, i)))
arr[cnt++] = i;
}
printf("Test Case #%d\n", t);

if (cnt)
{
printf("The winning moves are:");
for (i = 0; i < cnt; i++)
printf(" %d", arr[i]);
printf("\n");
} else
printf("There's no winning move.\n");
printf("\n");
}

return 0;
}
