编程之美 - 抓石头游戏(3)

游戏规则: 
有两堆石头,玩家A 和 B,两个人可以从一堆石头中取任意数目的石头或从两堆石头中取相同数量的石头。
最后取得所有石头的人胜。

书中的分析
从最简单的情况入手
只有一块石头  == >  先拿的一定会赢。
如果两堆石头数目相等   X:X  ==> 先拿的一定会
如果两堆石头数目为 1 和 2   ==> 先拿的一定会

那么1 和 2 就是我们找到的第一个希望留给对手的组合。

书中使用了 表格的形式来进行分析,非常的有效。

1,1 1,2 1,3 1,4 1,5 1,6 1,7 1,8 1,9 1,10

2,2 2,3 2,4 2,5 2,6 2,7 2,8 2,9 2,10


3,3 3,4 3,5 3,6 3,7 3,8 3,9 3,10



4,4 4,5 4,6 4,7 4,8 4,9 4,10




5,5 5,6 5,7 5,8 5,9 5,10





6,6 6,7 6,8 6,9 6,10






7,7 7,8 7,9 7,10







8,8 8,9 8,10








9,9 9,10









10,10

第一轮可以把相等的情况都去掉了, 1,2是找到的第一个目标

1,2 1,3 1,4 1,5 1,6 1,7 1,8 1,9 1,10


2,3 2,4 2,5 2,6 2,7 2,8 2,9 2,10



3,4 3,5 3,6 3,7 3,8 3,9 3,10




4,5 4,6 4,7 4,8 4,9 4,10





5,6 5,7 5,8 5,9 5,10






6,7 6,8 6,9 6,10







7,8 7,9 7,10








8,9 8,10









9,10











第二轮可以去掉经过一次抓取可以将数目转换为1,2的所有的组合。相当于把必输的情况留给了对手。
例如: 2,3,从两堆各取1个石头,便成为了 1,2


1,2






















3,5 3,6 3,7 3,8 3,9 3,10





4,6 4,7 4,8 4,9 4,10






5,7 5,8 5,9 5,10







6,8 6,9 6,10








7,9 7,10









8,10





















经过第二轮后,3,5便是找到的第二组。
然后,第三轮就是去掉经过一次抓取可以将数目转换为1,2 或 3,5 的所有的组合。  又找到了 4,7


1,2






















3,5











4,7 4,8 4,9 4,10


















6,9 6,10









7,10































最后 在10以内的找到了  6,10。
[1,2]  [3,5]  [4,7]  [6,10]


总结成表:

i 1 2 3 4
x 1 3 4 6
y 2 5 7 10

这里可以看到两个规律
  • y = x + i
  • x 和 y 的集合是一个正整数的集合,而且不重复。


程序示例:

#include <iostream>
#include <vector>

using namespace std;

int getIndex(vector<int> arr, int val)
{
    int i = 0;
    for (i = 0; i < arr.size(); i++)
    {
        if (arr[i] == val) return i;
    }
    return -1;
}

bool calc(int x, int y)
{
    int t = 0, n = 1, i = 0;
    int delt = 1;
    vector<int> arrA;

    if (x == y)
        return true;

    if ((x == 1) && (y == 2))
        return false;

    if (x > y)
    {
        t = x; x = y; y = t;
    }

    arrA.push_back(2);
    cout << "[" << 1 << "," << 2 << "]" << endl;

    while(n < x)
    {
        while(getIndex(arrA, ++n) != -1);
        delt++;

        cout << "[" << n << "," << n + delt << "]" << endl;
        arrA.push_back(n + delt);
    }
    cout << endl;

    if ((n == x) && y==(n+delt))
        return false;
    else
        return true;

    return true;
}

int main()
{
    int i = 0;
    int Xs[] = {1, 1, 3, 3, 4, 6, 9,};
    int Ys[] = {1, 2, 5, 7, 8, 10, 10};
    int len = sizeof(Xs)/sizeof(Xs[0]);

    for (i = 0; i < len; i++)
    {
        if (calc(Xs[i], Ys[i]))
        {
            cout << Xs[i] << ":" << Ys[i] << "   win!" << endl;
        }
        else
        {
            cout << Xs[i] << ":" << Ys[i] << "   loose!" << endl;
        }

        cout << "=========================" << endl;
    }

    cin >> i;
    return 0;
}





测试结果:

1:1   win!
=========================
1:2   loose!
=========================
[1,2]
[3,5]

3:5   loose!
=========================
[1,2]
[3,5]

3:7   win!
=========================
[1,2]
[3,5]
[4,7]

4:8   win!
=========================
[1,2]
[3,5]
[4,7]
[6,10]

6:10   loose!
=========================
[1,2]
[3,5]
[4,7]
[6,10]
[8,13]
[9,15]

9:10   win!
=========================



备注:书中在最后判断 x,y 是否会赢时用的代码有些问题:
原来的代码是

if ((n != x) || getIndex(arrA, y) != -1)
    return true; 
else
    return false;


在测试用例 x = 9, y = 10时会失败,[6, 10]   [9, 15]是两对组合,当x = 9时, 10也在arrA中,这时用getIndex(y)取得的值不是-1,判断是反的。


修改了一下
   if ((n == x) && y==(n+delt))
        return false;
    else
        return true;



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值