巴什博弈是最基础的博弈游戏。
有一堆石子,共计n颗,规定二人每次拿1~m颗石头,先拿完者胜,求解先手是否能赢。
算法思想非常好考虑:
因为每次最多拿m颗石头,所以当n是m+1的倍数时,first无论拿多少颗,second只要在first之后拿能够凑够m+1颗石子就能胜利,所以first必败。
如果n不是m+1的倍数,假设n = (m+1)*s + r(1 <= r <= m-1),那么first只要在首轮拿走r颗石子,使得此时石子数目为m+1的倍数,就将必败态转移给了second,first延续上述的策略,first必胜。
参考题目:
HDU1849
#include
#include
using namespace std;
int main()
{
int N;
cin>>N;
while(N--)
{
int n,m;
scanf("%d %d",&n,&m);
if(n %(m+1) == 0)
{
cout<<"second"<
HDU2149
#include
#include
#include
#include
using namespace std;
const int maxn = 1005;
int main()
{
int m,n;
//田地m
while(scanf("%d %d",&m,&n) != EOF)
{
if(m<=n)
{
for(int i = m; i
有两堆石子,两个玩家可以(1)从两堆石子中分别取相同数目的石子(2)从一堆石子中取任意数目的石子,求解先手是否能赢。
算法思想很巧(而且数学思想很BT):
首先我们引入奇异局势(也就是必败局势,我实在是很不明白,为什么好好的名词非要按字面翻译成各种令人费解的名字)的概念:可以知道,(0,0)一定是一个奇异局势,以此类推(0,i),(i,i),(i,0)就一定是必胜局势。因必胜局势的上一步便是奇异局势,故我们可以得到奇异局势如下:(0,0)、(1,2)、(3,5)、(4,7)、(6,10)、(8,13)、(9,15)、(11,18)、(12,20)……
可以发现(我没发现),奇异局势(ak,bk)是这样构成的:(ak,ak+k),ak是在前面未出现过的最小自然数。
奇异局势有以下三条性质:
- 任何自然数都包含在唯一一个奇异局势之中。
- 任意操作都可将奇异局势变为非奇异局势。
- 方法适当,非奇异局势可以变为奇异局势。
假设面对的局势是(a,b),若 b = a,则同时从两堆中取走 a 个物体,就变为了奇异局势(0,0);
如果a = ak ,b > bk,那么,取走b – bk个物体,即变为奇异局势;
如果 a = ak , b < bk ,则同时从两堆中拿走 ak – ab + ak个物体,变为奇异局势( ab – ak , ab – ak+ b – ak);
如果a > ak ,b= ak + k,则从第一堆中拿走多余的数量a – ak 即可;如果a < ak ,b= ak + k,分两种情况,第一种,a=aj (j < k),从第二堆里面拿走 b – bj 即可;第二种,a=bj (j < k),从第二堆里面拿走 b – aj 即可。
以上关于非奇异到奇异的拿法是我粘的,总之,判断(a,b)是否为奇异矩阵的公式如下:,如果成立,则此局面为奇异局势,先手必败。
参考题目:
#include
#include
#include
using namespace std;
int main()
{
long long n,m;
while(scanf("%I64d %I64d",&n,&m) != EOF)
{
if(n
尼姆博弈可以说是巴什博弈的加强版,即有N堆石子,可以从一堆石子中拿走任意个,先拿完者胜
算法推导可以见这位博主写的博文POJ-2234:
一个格局记为(x1,x2,...,xn),且次序无关,此格局为 P-格局 当且仅当 x1^x2^...^xn = 0.其中^是按位异或运算
参考题目:
POJ2234
#include
#include
#include
#include
using namespace std;
const int maxn =1005;
const int INF =1e9;
int main()
{
int n;
while(scanf("%d",&n) != EOF)
{
int a[maxn];
int ans = 0;
for(int i = 1; i<=n; i++)
{
scanf("%d",&a[i]);
ans ^= a[i];
}
if(ans) printf("Yes\n");
else printf("No\n");
}
return 0;
}
HDU1850
#include
#include
#include
#include
using namespace std;
const int maxn = 105;
int main()
{
int n;
while(scanf("%d",&n) != EOF && n)
{
int a[maxn];
int tmp = 0;
int sum = 0;
for(int i = 1; i<=n; i++)
{
cin>>a[i];
tmp ^= a[i];
}
if(tmp == 0)
cout<<"0"<(tmp^a[i]))
sum++;
}
cout<
1堆石子有n个(n>1),两人轮流取.先取者第1次可以取任意多个,但不能全部取完.以后每次取的石子数不能超过上次取子数的2倍,先取完者胜。
算法如下:
判断石子数是否为斐波那契数即可,若n = f(k),则先手必败。
因为在斐波那契数列中,f(i) = f(i - 1) + f (i - 2),所以很好证明f(i) <= f(i-1)*2.
参考题目
HDU2516
#include
#include
#include
using namespace std;
int main()
{
long long fib[120];
fib[1] = 1;
fib[2] = 1;
for(int i = 3; i<120; i++)
fib[i] = fib[i-1] + fib[i-2];
int n;
while(scanf("%d",&n) != EOF)
{
if(n==0)break;
int flag = 0;
for(int i = 1; i<120; i++)
{
if(fib[i] == n)
{
flag = 1;
break;
}
}
if(flag)
cout<<"Second win"<
n个石子围成一个环,每次可取走1个或相邻的两个,注意若两个石子之间的石子被取走,这两个石子仍然是不相邻的。
算法比较精妙,值得借鉴
对于n=1和n=2的情况,先手必胜。
当n=3时,显然先手必败。
当n>3时,我们可以对这个环画一个对称轴,无论先手怎么取,后手只要让这个环呈对称状就能赢。
参考题目:
POJ2484
#include
#include
#include
#include
using namespace std;
const int maxn = 1005;
int main()
{
int n;
while(scanf("%d",&n)!= EOF &&n)
{
if(n==1||n==2)
cout<<"Alice"<
HDU3951
#include
#include
#include
#include
using namespace std;
const int maxn =1005;
const int INF =1e9;
int main()
{
int N;
while(scanf("%d",&N) != EOF)
{
for(int t = 1; t<= N; t++)
{
int n,k;
cin>>n>>k;
if(k == 1)
{
if(n%2)
printf("Case %d: first\n",t);
else
printf("Case %d: second\n",t);
}
else
{
if(n>k)
printf("Case %d: second\n",t);
else
printf("Case %d: first\n",t);
}
}
}
return 0;
}