问题描述:有两堆石子,数量任意,可以不同。游戏开始由两个人轮流取石子。游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子;二是可以在两堆中同时取走相同数量的石子。最后把石子全部取完者为胜者。现在给出初始的两堆石子的数目,如果轮到你先取,假设双方都采取最好的策略,问最后你是胜者还是败者。
看起来这很像一种益智游戏,可以依靠枚举来进行尝试,从而找到最终答案,但其中蕴含着一个很有意思的规律,使得这个问题可以依靠代码来运算,那个有趣的规律就是威佐夫博弈.
这个博弈中有个概念叫做奇异局势,就是当是奇异局势时,先拿着必输,当不是时,则反之。这个结论的应用非常简单了,但是如何证明一个局势是奇异局势,就非常的复杂。
奇异局势的性质
1。任何自然数都包含在一个且仅有一个奇异局势中。
由于a[k]是未在前面出现过的最小自然数,所以有a[k] > a[k-1] ,而 b[k]= a[k] + k > a[k-1] + k > a[k-1] + k - 1 = b[k-1] > a[k-1] 。所以性质1成立。
2。任意操作都可将奇异局势变为非奇异局势。
事实上,若只改变奇异局势(a[k],b[k])的某一个分量,那么另一个分量不可能在其他奇异局势中,所以必然是非奇异局势。如果使(a[k],b[k])的两个分量同时减少,则由于其差不变,且不可能是其他奇异局势的差,因此也是非奇异局势。
3。采用适当的方法,可以将非奇异局势变为奇异局势。
假设面对的局势是(a,b),若 b = a,则同时从两堆中取走 a 个物体,就变为了奇异局势(0,0);如果a = a[k] ,b > b[k] 那么,取走b - b[k]个物体,即变为奇异局势;如果 a = a[k] , b < b[k] 则同时从两堆中拿走a-a[b-a](注:这里b-a是a的下标, 不是a*(b-a)) 个物体变为奇异局势( a[b-a], b-a+a[b-a]);如果a > a[k] ,b= a[k] + k 则从第一堆中拿走多余的数量a - a[k] 即可;如果a < a[k] ,b= a[k] + k,分两种情况,第一种,a=a[j] (j < k)从第二堆里面拿走 b - b[j] 即可;第二种,a=b[j] (j < k)从第二堆里面拿走 b - a[j] 即可。
结论:两个人如果都采用正确操作,那么面对非奇异局势,先拿者必胜;反之,则后拿者取胜。
最后利用beatty序列得出判断局势是否为奇异局势的式子:ak =[k(1+√5)/2],bk= ak + k (k=0,1,2,...n 方括号表示取整函数)
所以一旦两堆石子确定,两边都采取正确方法,结局就已经唯一确定。
动态规划
我们从例题数字三角形来体会动态规划的意义,有一个金字塔型的的三角形数字塔,每一层都比上一层多一个,除最后一层,每一层每一个都有两个“脚”相对应,如何选择哪只“脚”,使得从上往下一串数字之和最大,这里就用到了动态规划,从下往上,显然每次都会生成两个可能,每次都挑选最大的相加,最后最大的就是最上面那个数字。
#include<iostream>
#include<algonithm>
using namespace std;
#define MAX 101
int D[MAX][MAX];
int n; int *maxsum;
int main(){
int i,j;
cin>>n;
for(i=1;i<=n;i++)
for(j=1;j<=i;j++)
cin>> D[i][j]; /*数字输入*/
maxsum=D[n];
for( int i=n-1;i>=1;--i)
for(int j=1;j<=i;++j)
maxsum[j] = max(maxsum[j],maxsum[j+1])+D[i][j]; /* 递归转化为递推*/
cout << maxsum[1]<<end1; /*最终输出最终结果30*/
俞