
团体尼姆赛:传统的尼姆游戏由两名玩家进行,在一堆石头中,双方轮流取走任意合法数量块石头,取走最后一块石头的玩家落败。多人尼姆游戏将参赛人数拓展至两个队伍,每支队伍有n名队员交错入座,单次分别能最多取走Mi块石头,取走S块石头中的最后一块的队伍失败,求第一支队伍是否有必胜策略?
4.2找出游戏的必胜策略
推理与动态规划算法
动态规划地搜索整个状态树,定义
win[k][j][i]:=第k支队伍在轮到第j名队员且剩余i块石头的状态
如果i等于0,那么这就是必胜态。以此为初始状态往i=S的方向递推,递推规则为:如果当前状态的后续状态中有一个为必败态,则当前状态为必胜态。理解起来很简单,后续状态指的是对方的状态,只要对方有一个必败态,我方就可以转移到此后续状态,让对方落败,我方取胜。
#include <iostream>
using namespace std;
const int MAX_N = 10;
const int MAX_S = 1 << 13;
int n, S;
int M[2][MAX_N];
bool win[2][MAX_N][MAX_S]; // win[k][j][i]:=第k支队伍在轮到第j名队员且剩余i块石头的状态
bool input()
{
scanf("%d%d", &n, &S);
for (int i = 0; i < n; ++i)
{
scanf("%d%d", M[0] + i, M[1] + i);
}
return n > 0;
}
bool solve()
{
for (int i = 0; i <= S; ++i) // 石头数量
{
for (int k = 0; k < 2; ++k) // 队伍编号
{
for (int j = 0; j < n; ++j) // 队员编号
{
if (i == 0)
{
win[k][j][i] = true; // 没有石头,必胜态
continue;
}
win[k][j][i] = false;
const int nxt_k = k ^ 1; // 下一支队伍的编号
const int nxt_j = (k == 1 ? (j + 1) % n : j); // 下一个队员的编号
for (int t = 1; t <= M[k][j] && i - t >= 0; ++t) // 尝试拿走1到M[k][j]个石头
{
win[k][j][i] |= !win[nxt_k][nxt_j][i - t]; // 只要后续状态有一个必败态,则当前为必胜态
}
}
}
}
return win[0][0][S];
}
///////////////////////////SubMain//////////////////////////////////
int main(int argc, char *argv[])
{
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
while (input())
puts(solve() ? "1" : "0");
#ifndef ONLINE_JUDGE
fclose(stdin);
fclose(stdout);
system("out.txt");
#endif
return 0;
}
///////////////////////////End Sub//////////////////////////////////
| 14814280 | hankcs | 2068 | Accepted | 288K | 32MS | C++ | 1244B | 2015-10-14 19:59:51 |
码农场