有三堆石子,分A,B,C三堆,两人进行取石子游戏,每次从其中一堆取任意多个(至少取一个,可以取完)如果应该你取石子时,已经没有石子可取,则你输。如果你第一个取,问你是否能赢,如果能赢,输出第一次的取法否则输出1
经典博弈问题,同类问题有:
1.一堆石子每次取1~k个,如果应该你取石子时,已经没有石子可取,则你输。
2.两堆石子每次从其中一堆取个任意多个(至少取一个,可以取完),如果应该你取石子时,已经没有石子可取,则你输。
3.n堆石子每次从其中一堆取个任意多个(至少取一个,可以取完),如果应该你取石子时,已经没有石子可取,则你输。
其实都是同一类问题,既寻找奇异局势。
例如1中,无石子可取的情况为0,同时无论对方如何取,你都可以令你们俩这一回合一共取(k+1)个石子,即当剩余k+1个石子时对方无法直接取完而且无论如何取,你都可以将剩下的直接取完,即你赢,所以(k+1)的情况可以认为是0,重复上方的推论,我们可以得到当对方取石子时石子的个数是(k+1)的倍数则对方必输,因此当你取石子时,只要石子数不是(k+1)的倍数就必赢即将石子数取到(k+1)的倍数
2中无石子可取情况为(0,0)那么我们自然的想到如果两堆石子的数目相等那么只要跟着首先取石子的人取,则后手必应即若先手取第一堆i个,则后手取第2堆i个。所以当两堆石子数相等时先手输,否则后手输。
3当为k堆时,我们可以使用异或进行运算,我们能得到k堆的石子数异或的结果,如果异或结果为0则表示后手赢否则先手赢,原因是,当异或结果为0时,先手无论如何拿都不能使异或结果依然为0,而当石子全部被拿完时,异或的结果必定为0,因此就表示先手不能把石子全部拿完;而当后手拿时,由于先手拿完后,异或结果不为0,则后手只要拿可以令当前异或结果为0的石子数即可,因此最后把石子全部拿完的必定是后手。
本题就是第3种类型
代码:
#include <iostream>
#include <algorithm>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <set>
#include <string>
#include <queue>
#include <vector>
#define ll long long
#define Max 100005;
using namespace std;
struct st{
int nu;
char s;
};
bool cmp(st a,st b)
{
return a.nu<=b.nu;
}
int main()
{
st a[4];
scanf("%d,%d,%d",&a[0].nu,&a[1].nu,&a[2].nu);
a[0].s='A';
a[1].s='B';
a[2].s='C';
sort(a,a+3,cmp);
if(a[0].nu!=0||a[1].nu!=0||a[2].nu!=0)
{
int s,s1,s2,s3;///由于本题要输出第一步的操作所以应当将能使异或结果为0的操作输出
s=a[0].nu^a[1].nu;
s1=a[0].nu^a[2].nu;
s2=a[2].nu^a[1].nu;
s3=s^a[2].nu;
if(s3==0)
printf("1\n");
else
{
if(a[0].nu-s2>=0)///表示拿第一堆可以让当前异或结果
printf("%c,%d\n",a[0].s,a[0].nu-s2);
else if(a[1].nu-s1>=0)///拿第二堆
printf("%c,%d\n",a[1].s,a[1].nu-s1);
else if(a[2].nu-s>=0)///拿第三堆
printf("%c,%d\n",a[2].s,a[2].nu-s);
}
}
return 0;
}