Topcoder FollowingNim

前置知识

SG函数
Nim问题

题意

  n堆石子,进行nim游戏。不同于普通nim游戏,有m种特殊操作,如果一个人对某一堆石子进行特殊操作,那么下一个人只能在这堆石子上进行操作。无法进行操作的一方失败。统计第一步有多少种走法使得先手必胜。

思路

  1. 考虑两个人只能进行特殊操作的情况
    求出相应的SG函数。如果某堆石子的SG函数不为0,那么称这堆石子是特殊先手必胜态。

  2. 如果某一堆石子是特殊先手必胜态的,那么当前整体局势先手必胜。
    假设当前有一堆石子(数量为x)是特殊先手必胜态的。
      (1) 存在 0 < x ′ ≤ x 0 <x' \leq x 0<xx,使得操作后局势后B手必败。如果x-x’不是特殊操作,A操作x- x’后,无论B如果操作都会失败。如果x-x’是特殊操作,那么限制会更强,B更不可能获胜。
      (2) 对于任意 0 < x ′ ≤ x 0<x'\leq x 0<xx,操作后局势后手必胜。根据Nim游戏的必胜策略,后手必胜的操作策B略必然在不同堆种进行,A如果此时先手使用特殊操作,强行要求B在同一堆种进行操作,那么B不一定会有必胜策略。此时,B将达到和A一样的处境,B也必须使用特殊操作。
    综上,当某一堆石子是特殊先手必胜态的时候,当前整体局势是先手必胜态。

  3. 如果当前局势中没有特殊先手必胜态,那么A进行操作时,不会使其转移到特殊先手必胜态,否则B必胜。所以两个人在操作的过程中,都会尽量避免其转化成特殊先手必胜态。

  4. 假设某一堆石子不是特殊先手必胜态。如果A在这一堆上进行特殊操作,那么这堆石子就会转移成特殊先手必胜态,此时B有必胜策略。因此,A不会对一堆不是特殊先手必胜态的石子进行特殊操作。
    结论:如果当前有一堆石子是特殊先手必胜态,那么先手获胜。否则,先手会使用非特殊操作,并且操作之后避免产生特殊先手必胜态,因此,接下来的局势中依旧没有特殊先手必胜态,后手依旧采取非特殊操作并避免特殊先手必胜态的产生。

实现

  1. 预处理
      求仅进行特殊操作的情况下的SG函数,以此得出特殊先手必胜态。
      求仅使用非特殊操作且尽量避免特殊先手必胜态的产生的情况下的SG函数,作为是否存在最优策略的判断依据。
  2. 枚举方案
      枚举A在第i堆取j个石子。
      (1) 如果 p i l e s i − j piles_{i}-j pilesij使得这一堆石子转化成了先手必胜态,那么A失败。
      (2) 当前一步不是特殊操作:如果其他堆中没有特殊先手必胜态,且当前局势的SG函数异或和为0那么A必胜。
      (3) 当前一步是特殊操作:如果其他堆中有特殊先手必胜态,那么先手必胜。否则,枚举B的操作,判断SG函数异或和,如果B没有必胜策略,那么A获胜。

代码

求有限制的SG函数

		vector<int> get_grundy(int n, vector<int> a, vector<int> allow)
		{
			vector<int> sg(n + 1, 0);
			int vist[N];
			sg[0] = 0;
			for(int i = 1; i <= n; i++)
			{
				for(int j = 0; j <= n; j++)	vist[j] = 0;
				for(int j = 0; j < a.size(); j++)	if(i >= a[j] && allow[i - a[j]] == 0)	vist[sg[i - a[j]]] = 1;
				for(int j = 0; j <= n; j++)	if(vist[j] == 0)	{	sg[i] = j; 	break;} 
			}
            return sg;
		}

整体代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
const int N = 110;

class FollowingNim
{
	public:
		vector<int> get_grundy(int n, vector<int> a, vector<int> allow)
		{
			vector<int> sg(n + 1, 0);
			int vist[N];
			sg[0] = 0;
			for(int i = 1; i <= n; i++)
			{
				for(int j = 0; j <= n; j++)	vist[j] = 0;
				for(int j = 0; j < a.size(); j++)	if(i >= a[j] && allow[i - a[j]] == 0)	vist[sg[i - a[j]]] = 1;
				for(int j = 0; j <= n; j++)	if(vist[j] == 0)	{	sg[i] = j; 	break;} 
			}
            return sg;
		}
		int countWinningMoves(vector<int> piles, vector<int>special)
		{
			vector<int> s_win = get_grundy(100, special, vector<int>(100 + 1, 0));
			vector<int> not_special;
			for(int i = 1; i <= 100; i ++)
				if(find(special.begin(), special.end(), i) == special.end())
					not_special.push_back(i);
			vector<int>sg = get_grundy(100, not_special, s_win);
			int xor_sum = 0;
			for(int i = 0; i < piles.size(); i ++)	xor_sum ^= sg[piles[i]];
			int cnt = 0;
			for(int i = 0; i < piles.size(); i++)	if(s_win[piles[i]])		cnt++;
			int ans = 0;
			for(int i = 0; i < piles.size(); i++)
			{
				for(int j = 1; j <= piles[i]; j++)
				{
					if(s_win[piles[i] - j])		continue;
					int now = cnt;
					if(s_win[piles[i]])	now --;
					if(find(special.begin(), special.end(), j) == special.end())
						if(now == 0 && (xor_sum ^ sg[piles[i]] ^ sg[piles[i] - j]) == 0)	ans++;
					else
					{
                        if(now > 0)	ans++;
                        else
                        {
							int flag = 0;
							for(int k = 0; k < not_special.size(); k++)
							{
								if(piles[i] < j + not_special[k])	continue;
								if((xor_sum ^ sg[piles[i]] ^ sg[piles[i] - j - not_special[k]]) == 0)		flag = 1;
							}
							if(flag == 0)	ans ++;
                       	 }
					 } 
				}
			}
			return ans;
		}
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值