Return of the Nim----博弈(2017山东ACM-ICPC省赛)

Return of the Nim
Time Limit: 1000MS Memory Limit: 65536KB
Problem Description

Sherlock and Watson are playing the following modified version of Nim game:

There are n piles of stones denoted as ,,...,, and n is a prime number;
Sherlock always plays first, and Watson and he move in alternating turns. During each turn, the current player must perform either of the following two kinds of moves:
    Choose one pile and remove k(k >0) stones from it;
    Remove k stones from all piles, where 1≤k≤the size of the smallest pile. This move becomes unavailable if any pile is empty.
Each player moves optimally, meaning they will not make a move that causes them to lose if there are still any better or winning moves.

Giving the initial situation of each game, you are required to figure out who will be the winner
Input

The first contains an integer, g, denoting the number of games. The 2×g subsequent lines describe each game over two lines:
1. The first line contains a prime integer, n, denoting the number of piles.
2. The second line contains n space-separated integers describing the respective values of ,,…,.

1≤g≤15
2≤n≤30, where n is a prime.
1≤pilesi≤ where 0≤i≤n−1

Output

For each game, print the name of the winner on a new line (i.e., either “Sherlock” or “Watson”)
Example Input

2
3
2 3 2
2
2 1

Example Output

Sherlock
Watson

Hint
Author
“浪潮杯”山东省第八届ACM大学生程序设计竞赛(感谢青岛科技大学)

题意:有几堆石头,Sherlock and Watson ,每人拿一次石块一共有两种拿法,1:可以选择从一堆里拿任意的石块2:也可以选择每一堆拿k个石块但是k小于最小堆石块的个数。最后那石块的人为胜利。现在给你多组数据让你判断谁胜利(每一组数据都可以判断出谁一定能胜利)。
在解题目之前要先知道两个知识。1:威左夫博弈2:Nim游戏。

威佐夫博弈(Wythoff Game):有两堆各若干个物品,两个人轮流从某一堆或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
对于威左夫博弈来说:我们用(ak,bk)(ak ≤ bk ,k=0,1,2,…,n)表示两堆物品的数量并称其为局势,如果甲面对(0,0),那么甲已经输了,这种局势我们称为奇异局势。前几个奇异局势是:(0,0)、(1,2)、(3,5)、(4,7)、(6,10)、(8,13)、(9,15)、(11,18)、(12,20)。
可以看出,a0=b0=0,ak是未在前面出现过的最小自然数,而 bk= ak + k。两个人如果都采用正确操作,那么面对非奇异局势,先拿者必胜;反之,则后拿者取胜。
那么任给一个局势(a,b),怎样判断它是不是奇异局势呢?我们有如下公式:
ak =[k(1+√5)/2],bk= ak + k (k=0,1,2,…n 方括号表示取整函数)
奇妙的是其中出现了黄金分割数(1+√5)/2 = 1.618…因此,由ak,bk组成的矩形近似为黄金矩形,由于2/(1+√5)=(√5-1)/2,可以先求出j=[a(√5-1)/2],若a=[j(1+√5)/2],那么a = aj,bj = aj + j,若不等于,那么a = aj+1,b = aj + j + 1,若都不是,那么就不是奇异局势。然后再按照上述法则进行,一定会遇到奇异局势。

Nim游戏:通常的Nim游戏的定义是这样的:有若干堆石子,每堆石子的数量都是有限的,合法的移动是“选择一堆石子并拿走若干颗(不能不拿)”,如果轮到某个人时所有的石子堆都已经被拿空了,则判负(因为他此刻没有任何合法的移动)。
这游戏看上去有点复杂,先从简单情况开始研究吧。如果轮到你的时候,只剩下一堆石子,那么此时的必胜策略肯定是把这堆石子全部拿完一颗也不给对手剩,然后对手就输了。如果剩下两堆不相等的石子,必胜策略是通过取多的一堆的石子将两堆石子变得相等,以后如果对手在某一堆里拿若干颗,你就可以在另一堆中拿同样多的颗数,直至胜利。如果你面对的是两堆相等的石子,那么此时你是没有任何必胜策略的,反而对手可以遵循上面的策略保证必胜。如果是三堆石子……好像已经很难分析了,看来我们必须要借助一些其它好用的(最好是程式化的)分析方法了,或者说,我们最好能够设计出一种在有必胜策略时就能找到必胜策略的算法。
定义P-position和N-position,其中P代表Previous,N代表Next。直观的说,上一次move的人有必胜策略的局面是P-position,也就是“后手可保证必胜”或者“先手必败”,现在轮到move的人有必胜策略的局面是N-position,也就是“先手可保证必胜”。更严谨的定义是:1.无法进行任何移动的局面(也就是terminal position)是P-position;2.可以移动到P-position的局面是N-position;3.所有移动都导致N-position的局面是P-position。
按照这个定义,如果局面不可能重现,或者说positions的集合可以进行拓扑排序,那么每个position或者是P-position或者是N-position,而且可以通过定义计算出来。

题目解析:当有两堆石块时,如果去掉第一种操作则是裸地威左夫博弈。当石块的堆数n>=3的时候,如果去掉第二种操作则是裸地Nim游戏。

假如说现在有三堆,个数分别是15,6,9,然后我们可以发现这已经在Nim的平衡状态了,我们写成二进制更好看一些
1 1 1 1
0 1 1 0
1 0 0 1
我们随便取一个k,假如说就取2,那么已经在Nim平衡状态的这个状态肯定会被破坏,就是不平衡了,单看倒数第二位,前两个数的倒数第二位为0,最后一个数的倒数第二位为1 ,只看倒数第二位就知道三个数的异或和一定不为0,所以不在平衡状态,又因为题目保证了素数(其实这里挺坑的,奇数堆就可以),所以如果已经在平衡状态的话,减k操作和普通的Nim操作一定会破坏平衡状态,如果不在平衡状态的话,普通的Nim操作或者是减k操作一定可以达到一种平衡状态(普通的Nim操作就可以,减k有的时候也可以,比如7,6,5所有堆同时减去4就达到了平衡状态,单把最后一堆减4个也是平衡状态),然后发现减k操作好像对于Nim博弈来说…并没有什么影响,所以这个题的题解就是如果只有两堆,就是裸地威佐夫博弈,如果是大于两堆,就是裸地Nim。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
int a[1000];
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        int n;
        scanf("%d",&n);
        for(int i=0;i<n;i++){
            scanf("%d",&a[i]);
        }
        if(n==2){
            if(a[0]<a[1]){
                swap(a[0],a[1]);
            }
            if(floor((a[0]-a[1])*((sqrt(5.0)+1.0)/2.0))!=a[1]){
                printf("Sherlock\n");
            }
            else{
                printf("Watson\n");
            }
        }
        else{
            int k=a[0];
            for(int i=1;i<n;i++){
                k^=a[i];
            }
            if(k==0){
                printf("Watson\n");
            }
            else{
                printf("Sherlock\n");
            }
        }
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值