HDU - 4388(Stone Game II)规律型博弈

题意:

给出n堆物品,每堆物品都有若干件,玩家A和B进行游戏,每人每轮操作一次,按照如下规则:

  1. 任意选择一个堆,假设该堆有x个物品,从中选择k个,要保证0<k<x且0<(x xor k)<x。
  2. 再增加一个大小为x xor k的堆(也就相当于将一个x个物品的堆变成一个k个物品的堆和一个x xor k个物品的堆),另外有一个技能,可以将这个大小为x^k的堆变成(2*k)^x的堆,但是这个技能每个人只有一次机会可以使用。

现在问两人轮流操作,都采取最优策略,最后不能操作的人输,问先手能不能赢。

 题解:

答案:假设第i堆有x个物品,cnt为x二进制中1的个数,统计这n堆所有(cnt-1)的和,和为偶数后手赢,奇数先手赢。

解释:

首先我们需要知道:

  • 将x个物品的堆变为一个k个物品的堆和一个x xor k个物品的堆,前后二进制中‘1’的个数的奇偶性不变。
  • 将x xor k变为 x xor (2*k),前后二进制中‘1’的个数的奇偶性不变。

那么我们先假设只有一堆物品(n=1),假设这堆有9个物品(x=9二进制为1001),那么k只能等于1000或1,x xor k只能等于1或1000,这时我们会发现,当一个数的二进制中‘1’的个数只有一个时,就不能继续分了,则为必败态。

因此,由于所有的操作都不会改变二进制中‘1’个数的奇偶性,那么操作次数的奇偶性一定与所有(cnt-1)的和的奇偶性相同。

而且最终结果一定是分成了许多个二进制中只有一个‘1’的数。

 代码:

#include <set>
#include <map>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <string>
#include <cstdio>
#include <cstring>
#include <sstream>
#include <iomanip>
#include <iostream>
#include <algorithm>
#define clr(str,x) memset(str,x,sizeof(str))
#define FRER() freopen("in.txt","r",stdin);
#define FREW() freopen("out.txt","w",stdout);
#define MAX_INF 0x7fffffff
#define INF 0x3f3f3f3f
#define maxn 100
using namespace std;
int Get(int x)
{
    int cnt=0;
    while(x>0)
    {
        cnt+=x%2;
        x/=2;
    }
    return cnt;
}
int main()
{
    int T;
    scanf("%d",&T);
    for(int Case=1; Case<=T; Case++)
    {
        int n,ans=0;
        scanf("%d",&n);
        for(int i=0; i<n; i++)
        {
            int x;
            scanf("%d",&x);
            ans+=Get(x)-1;
        }
        if(ans%2==0) printf("Case %d: No\n",Case);
        else printf("Case %d: Yes\n",Case);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值