黑白棋(game)

黑白棋(game)
【问题描述】
小A 和小B 又想到了一个新的游戏。
这个游戏是在一个1*n 的棋盘上进行的,棋盘上有k 个棋子,一半是黑色,
一半是白色。

最左边是白色棋子,最右边是黑色棋子,相邻的棋子颜色不同。


小A 可以移动白色棋子,小B 可以移动黑色的棋子,他们每次操作可以移动
1 到d 个棋子。
每当移动某一个棋子时,这个棋子不能跨越两边的棋子,当然也不可以出界。
当谁不可以操作时,谁就失败了。
小A 和小B 轮流操作,现在小A 先移动,有多少种初始棋子的布局会使他胜
利呢?
【输入格式】
共一行,三个数,n,k,d。

【输出格式】

输出小A 胜利的方案总数。答案对1000000007 取模。
【样例输入】
10 4 2
【样例输出】
182
【数据规模和约定】
对于30%的数据,有k=2。
对于100%的数据,有1<=d<=k<=n<=10000, k 为偶数,k<=100。


这是一道博弈论问题,所以进行分析来得到一些有用的结论

经过思考,可以发现游戏的终止状态是1号白棋和1号黑棋紧靠着,2号白棋和2号黑棋紧靠着,即每对黑白棋都仅靠着

这个时候先手必败,无论移动哪些棋子,对手下一步只要将对应棋子与其移动一样的距离就又回到仅靠的状态,这样重复多次后终会无法移动

还可以得出一个结论,白棋只有往右移才有意义,黑棋只有往左移才有意义,如果白棋往左移,那么黑棋只要对应的棋子向左移相同的步数,对应棋子之间的距离并没有改变,即局面没有改变,所以面临必败的先手这样做也无法挽回败局,必胜的先手肯定不会这么做增加变数

那么问题就变为了,有n堆石子,每次可以从1~k堆中取任意数量的石子(不能不取),什么情况是必胜局面

这个问题叫做nimk游戏

Nimk

Nim游戏的简单扩展,不过证明我还是想了一些时间的。

n堆石子,每次从不超过k堆中取任意多个石子,最后不能取的人算失败。
把n堆石子的石子数用二进制表示,统计每一二进制位上的1的个数,若每一位上1的个数mod
(k + 1)全为0,则必败。否则必胜
证明:
1.显然终止局面全为0满足命题,为必败态

2.对于某个局面,若存在某些二进制位上的1的个数mod(k +1)不为0,则一定存在一个合法的移动,使得每一个二进制位上的1的个数mod(k + 1)等于0。设1的个数mod(k +1)不为0的最高二进制位上有m个1,则把这些1都变成0,记此时改变的堆数为m,若遇到下一个1的个数mod(k +1)不为0的二进制位上有r个1,设原来改变的m堆在这一二进制位上有a个1和b个0。若a >=r,则把其中r个1->0;若b >= k + 1 - r,则把其中k +1 - r个0->1;否则,有a < r且b< k + 1 - r,选择原来改变的m堆以外的r - a堆,这r-a堆在该位上是1,此时改变的堆数为a+ b + r - a = b + r < k + 1 - r + r = k +1,故为合法的移动。重复上述操作,必然能使得每一位上的1的个数mod (k + 1)都为0。

3.对于某个局面,若每一个二进制位上的1的个数mod(k +1)都为0,则一定不存在某个合法的移动,使得移动后每一二进制位上的1的个数mod (k +1)都为0。因为最多对k堆石子做改变,所以不可能在某一位改变k + 1个1或0,使得mod(k +1)仍为0,必然需要做偶数个0->1和偶数个1->0,对于1->0的这堆,必然存在高位,从1->0,要使高位的操作成立,又需要某一堆在这一位上0->1,依次类推,最后在最高位上存在一堆是0->1,这是不合法的。
证毕。


这时候可以用动态规划来求方案数

用f[i][j]来表示考虑前i位k/2堆石子堆中一共j颗石子的必败局面有多少种

对于一组i,j,枚举当前位加起来是d+1的多少倍即可进行转移

所以必胜的总方案数为c(k,n)-sig(c(k/2,n-k-j+k/2)*f[i][j])(0<=j<=n-k,i为最高位,c为排列组合)


program game;
const
  maxn=1000000007;
var
  n,k,d,i,j,q:longint;
  ans:int64;
  anti,quick:array [0..20001] of int64;
  f:array [0..16,0..10001] of int64;

function min (a,b:int64):int64;inline;
begin
  if a<b then exit(a)
         else exit(b);
end;

function power (a:int64;b:longint):int64;inline;
var
  i:int64;
begin
  i:=1;
  while b<>0 do
    begin
      if b and 1 = 1 then i:=i*a mod maxn;
      b:=b div 2;
      a:=a*a mod maxn;
    end;
  exit(i);
end;

function c (m,n:longint):int64;inline;
begin
  exit(quick[n]*anti[m] mod maxn * anti[n-m] mod maxn);
end;

begin
  assign(input,'game.in');
  reset(input);
  assign(output,'game.out');
  rewrite(output);
  read(n,k,d);
  quick[0]:=1;
  for i:=1 to 20000 do
    quick[i]:=i*quick[i-1] mod maxn;
  for i:=0 to 20000 do
    anti[i]:=power(quick[i],maxn-2);
  f[0][0]:=1;
  for i:=1 to 15 do
    for j:=0 to n-k do
      for q:=0 to min(k div 2 div (d+1),j div ((1 shl (i-1))*(d+1))) do
        f[i][j]:=(f[i][j]+c(q*(d+1),k div 2)*f[i-1][j-q*(1 shl (i-1))*(d+1)]) mod maxn;
  ans:=0;
  for i:=0 to n-k do
    ans:=(ans+f[15][i]*c(k div 2,n-k-i+k div 2)) mod maxn;
  writeln(((c(k,n)-ans) mod maxn + maxn) mod maxn);
  close(input);
  close(output);
end.


  • 9
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
黑白棋,也叫反转棋,是一种非常经典的棋类游戏。下面是一个用Python编写的黑白棋代码: ```python import copy # 初始化棋盘 def initialize_board(): board = [[' ' for _ in range(8)] for _ in range(8)] board[3][3] = 'O' board[3][4] = 'X' board[4][3] = 'X' board[4][4] = 'O' return board # 打印棋盘 def print_board(board): print(' 0 1 2 3 4 5 6 7') print(' ----------------') for i in range(8): print(i, '|', end='') for j in range(8): print(board[i][j], end=' ') print('|') print(' ----------------') # 判断位置是否合法 def is_valid_move(board, row, col, color): if row < 0 or row >= 8 or col < 0 or col >= 8 or board[row][col] != ' ': return False other_color = 'O' if color == 'X' else 'X' directions = [(0, 1), (1, 0), (-1, 0), (0, -1), (1, 1), (1, -1), (-1, 1), (-1, -1)] for dx, dy in directions: x, y = row + dx, col + dy if x >= 0 and x < 8 and y >= 0 and y < 8 and board[x][y] == other_color: x, y = x + dx, y + dy while x >= 0 and x < 8 and y >= 0 and y < 8 and board[x][y] == other_color: x, y = x + dx, y + dy if x >= 0 and x < 8 and y >= 0 and y < 8 and board[x][y] == color: return True return False # 翻转棋子 def flip_pieces(board, row, col, color): other_color = 'O' if color == 'X' else 'X' directions = [(0, 1), (1, 0), (-1, 0), (0, -1), (1, 1), (1, -1), (-1, 1), (-1, -1)] for dx, dy in directions: x, y = row + dx, col + dy if x >= 0 and x < 8 and y >= 0 and y < 8 and board[x][y] == other_color: x, y = x + dx, y + dy while x >= 0 and x < 8 and y >= 0 and y < 8 and board[x][y] == other_color: x, y = x + dx, y + dy if x >= 0 and x < 8 and y >= 0 and y < 8 and board[x][y] == color: while x != row or y != col: x, y = x - dx, y - dy board[x][y] = color # 判断游戏是否结束 def is_game_over(board): for i in range(8): for j in range(8): if board[i][j] == ' ': return False return True # 判断谁是赢家 def get_winner(board): x_count = 0 o_count = 0 for i in range(8): for j in range(8): if board[i][j] == 'X': x_count += 1 elif board[i][j] == 'O': o_count += 1 if x_count > o_count: return 'X' elif x_count < o_count: return 'O' else: return 'No one' # 主函数 def main(): board = initialize_board() print_board(board) color = 'X' while not is_game_over(board): valid_moves = [] for i in range(8): for j in range(8): if is_valid_move(board, i, j, color): valid_moves.append((i, j)) if len(valid_moves) == 0: print("No valid move for", color) color = 'O' if color == 'X' else 'X' continue print("Valid moves for", color, ": ", valid_moves) while True: row = int(input("Enter row: ")) col = int(input("Enter column: ")) if (row, col) in valid_moves: break else: print("Invalid move, please try again.") board_copy = copy.deepcopy(board) flip_pieces(board_copy, row, col, color) board = board_copy print_board(board) color = 'O' if color == 'X' else 'X' winner = get_winner(board) print("Game over. The winner is", winner) if __name__ == '__main__': main() ``` 这个代码实现了一个双人黑白棋游戏,玩家可以轮流选择空白位置下棋,并翻转对手棋盘上相邻的棋子。游戏结束后,将根据棋盘上黑白棋子的数量判断胜负。在代码中使用了一个二维列表来表示棋盘,其中空白位置用空格字符表示,黑色棋子用'X'表示,白色棋子用'O'表示。代码中还包含了一些辅助函数,用于判断位置是否合法、翻转棋子、判断游戏是否结束以及判断胜负等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值