首页 > 代码库 > 【博弈论】BZOJ 1115-石子游戏

【博弈论】BZOJ 1115-石子游戏

题意:有N堆石子,除了第一堆外,每堆石子个数都不少于前一堆的石子个数。两人轮流操作 每次操作可以从一堆石子中移走任意多石子,但是要保证操作后仍然满足初始时的条件 谁没有石子可移时输掉游戏。问先手是否必胜。

 

 

这个题是一个阶梯博弈的问题。一开始没有接触过,各种度娘然后搞懂了,赶快记下来。

 

何为阶梯博弈?

简单的用Nim来说,一段台阶上,每个台阶上放了一堆石子,每次能从除了第一阶的台阶上取任意多石子向下放一级。无法操作的人输。

 

神犇告诉我,这个游戏的SG值就是奇数阶梯的SG值Xor起来...跪拜。

那么来证明一下。

 

如果只考虑奇数阶的情况我是必胜态,那么在这个游戏存在一个策略使我必胜——

  按只考虑奇数阶的策略行动。那么对手现在处于必败态。如果对手移动的是奇数阶的,依旧按照奇数阶的必胜策略行动。

  而如果对手移动的是偶数阶的,我可以将对手移动的石子再移动到下一阶。石子的数量和堆数都是之前我的状态,即对手的必败态。

  如此重复,偶数阶的最后一颗石子一定是我将某些数量的石子由2->1。

  可以看到,偶数阶对答案和决策并没有影响。

 

以上就是神奇的阶梯博弈!

 

 

那么这个题目需要稍微转换一下。

第i堆移走x个石子后,第i+1堆多了x个石子可以操作。是不是相当于将x个石子移动到了下一阶?

 

剩下的自己YY一下吧,贴个代码。

技术分享
 1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 #include <iostream> 5 using namespace std; 6  7 int ans,a[10005],n,i,T; 8  9 int main()10 {11     scanf("%d",&T);12     while (T--)13     {14         scanf("%d\n",&n); ans = 0;15         for (i = 1; i <= n; i++) scanf("%d",&a[i]);16 17         for (i = n; i > 1; i -= 2) ans ^= (a[i]-a[i-1]);18         if (n % 2) ans ^= a[1];19 20         if (ans) printf("TAK\n"); else printf("NIE\n");21     }22 23     return 0;24 }25             
BZOJ 1115

 

【博弈论】BZOJ 1115-石子游戏