题目大意:
两个人玩游戏。轮流取数字,范围在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;
}