HDU 2177 取(2堆)石子游戏 (威佐夫博弈)

20 篇文章 1 订阅



取(2堆)石子游戏

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2280    Accepted Submission(s): 1384


Problem Description
有两堆石子,数量任意,可以不同。游戏开始由两个人轮流取石子。游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子;二是可以在两堆中同时取走相同数量的石子。最后把石子全部取完者为胜者。现在给出初始的两堆石子的数目,如果轮到你先取,假设双方都采取最好的策略,问最后你是胜者还是败者。如果你胜,你第1次怎样取子? 
 

Input
输入包含若干行,表示若干种石子的初始情况,其中每一行包含两个非负整数a和b,表示两堆石子的数目,a和b都不大于1,000,000,且a<=b。a=b=0退出。
 

Output
输出也有若干行,如果最后你是败者,则为0,反之,输出1,并输出使你胜的你第1次取石子后剩下的两堆石子的数量x,y,x<=y。如果在任意的一堆中取走石子能胜同时在两堆中同时取走相同数量的石子也能胜,先输出取走相同数量的石子的情况.
 

Sample Input
  
  
1 2 5 8 4 7 2 2 0 0
 

Sample Output
  
  
0 1 4 7 3 5 0 1 0 0 1 2

   思路:威佐夫博弈,遇到奇异局势则必败第k个奇异局势(a(k), b(k)),a(k)是前面没有出现过的最小自然数(a(k)=(int)(k*(sqrt(5.0)+1)/2),b(k)=a(k)+k,采用适当的方法,这里不再证明,接下来只要判断就行了
同时从两堆中取相同数量的石头或者从一堆中取一定数量的石头,可以将非奇异局势变为奇异局势

#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;

double g = (sqrt(5.0) + 1) / 2;  // 黄金分割数1.618...

int main()
{
    int a, b;
    while(scanf("%d %d", &a, &b) == 2) {
        if(a == 0 && b == 0)
            break;

        int k = b - a;
        if(a == (int)(g * k))    // 只要判断a即可,因为b=a+k是恒成立的
        {
            printf("0\n");
        }
        else
        {
            printf("1\n");
            // 将非奇异局势变为奇异局势
            for(int i=1; i<=a; i++) // 同时从两堆中取相同数量的石头,注意这里是从1到a枚举
            {
                int x = a - i, y = b - i;
                int k = y - x;
                if(x == (int)(g * k))
                    printf("%d %d\n", x, y);
            }
            for(int i=b-1; i>=0; i--)   // 从一堆中取一定数量的石头,这里是从b-1往下枚举到0
            {
                int x = a, y = i;
                if(x > y)
                    swap(x, y);

                int k = y - x;
                if(x == (int)(g * k))
                    printf("%d %d\n", x, y);
            }
        }
    }
    return 0;
}


在网上看到一个代码,虽然很麻烦,但是把所有情况都写了出来


#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>

using namespace std;

int vis[1000005]= {0};

int main()
{
    //freopen("in.in","r",stdin);
    //freopen("out.out","w",stdout);
    for(int i=1; i<1000005; i++)
        vis[i]=(int)(i*1.0*(1+sqrt(5))/2);
    int n, m;
    while(scanf("%d%d",&n,&m)&&(n||m))
    {
        int k=m-n;
        if(vis[k]==n)
        {
            printf("0\n");
            continue;
        }
        printf("1\n");
        if(n>vis[k]&&m>vis[k]+k)
            printf("%d %d\n",vis[k],vis[k]+k);
        for(int i=1; i<1000005; i++)
            if(vis[i]==n)
            {
                if(m>vis[i]+i)
                    printf("%d %d\n",vis[i],vis[i]+i);
                break;
            }else if(vis[i]==m)
            {
                if(n>vis[i]+i)
                    printf("%d %d\n",vis[i],vis[i]+i);
                break;
            }
        for(int i=1; i<1000005; i++)
            if(vis[i]+i==m)
            {
                if(n>vis[i])
                    printf("%d %d\n",vis[i],vis[i]+i);
                break;
            }else if(vis[i]+i==n)
            {
                if(m>vis[i]+i)
                    printf("%d %d\n",vis[i],vis[i]+i);
                break;
            }
    }
    return 0;
}



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值