所有的博弈都是找到必败态,
如果当前的状态是必败态那么无论怎么操作都无法使游戏变成必胜态,
反之如果是必胜态则必然存在一步操作还使游戏变成必胜态。
下面说的就是三种基本的博弈:巴什博弈 威佐夫博奕 尼姆博弈
一:巴什博弈
有一堆石子,石子个数为n,两人轮流从石子中取石子出来,最少取一个,最多取m个。最后不能取的人输,问你最后的输赢情况。
这种就是可以直接判断必败态的问题。如果这堆石子少于或者等于m个,那么先手赢。如果石子数目为m+1个,那么先手必输,因为无论先手怎么拿石子,后手都可以直接把剩下的石子全部拿走。如果石子数目为m+1<n<2(m+1),那么先手就可以拿走n-(m+1)个石子,使得对手面对m+1的必败态,这样先手必赢。所以如此推算下去,我们就知道当n%(m+1)==0时,先手输,否则先手赢。
则n*(m+1)为必败态。先手取x,另外一个人就可以取y,使得取的石子的总数是m+1。
二:威佐夫博弈
有两堆石子,石子数目分别为n和m,现在两个人轮流从两堆石子中拿石子,每次拿时可以从一堆石子中拿走若干个,也可以从两中拿走相同数量的石子,拿走最后一刻石子的人赢。
可以发现,前面几组奇异局势为(a,b) :(0,0)、(1,2)、(3,5)、(4,7)、(6,10)、(8,13)、(9,15)、(11,18)、(12,20)
这其中第k组的a为前面没有出现过的最小非负整数,而b = a + k
两个人如果都采用正确操作,那么面对非奇异局势,先拿者必胜;反之,则后拿者取胜。
那么任给一个局势(a,b),怎样判断它是不是奇异局势呢?我们有如下公式:
ak =[k(1+√5)/2],bk= ak + k (k=0,1,2,...n 方括号表示取整函数)
详见:威佐夫博奕(百度百科)
取石子游戏 http://acm.bnu.edu.cn/bnuoj/problem_show.php?pid=1186
题目大意:威佐夫博奕
- #include<iostream>
- #include<cstdio>
- #include<cstring>
- #include<cmath>
- #include<map>
- #include<queue>
- #include<stack>
- #include<vector>
- #include<set>
- #include<ctype.h>
- #include<algorithm>
- #include<string>
- #define PI acos(-1.0)
- #define maxn 1000
- #define INF 1<<25
- #define mem(a, b) memset(a, b, sizeof(a))
- typedef long long ll;
- using namespace std;
- int main ()
- {
- int a, b;
- while(~scanf("%d%d", &a, &b))
- {
- if (a > b) swap(a, b); // 交换a,b 保证a比b要小
- int k = b - a;
- int n = (1 + sqrt(5.0)) / 2 * k; // 通过k来计算a。
- if (a == n) puts("0");
- else puts("1");
- }
- return 0;
- }
取(2堆)石子游戏 http://acm.hdu.edu.cn/showproblem.php?pid=2177
同样是取石头问题,不过如果先手能赢,要输出第一次取石子后所剩的情况。(如果在任意的一堆中取走石子能胜同时在两堆中同时取走相同数量的石子也能胜,先输出取走相同数量的石子的情况)
这道题目要输出威佐夫博弈从非奇异局势到奇异局势的转变方案。比直接判断复杂了很多,其实数据量不大,可以打表。不过还是采用推理的方式来解这道题。
思考过程:
判断从一堆中取出石头,这里其实可以分为三种。假设一开始的时候为(a,b) (b>a) 即默认括号里面第二个数大于第一个数 。第一种是从b当中取走少量的,变为(a,b - k)。第二种是从b中取走大量的,变为(b - t, a)。第三种是从a当中取走一些,变为(a - t,b)
- // 威佐夫博弈 + 输出方案
- #include<iostream>
- #include<cstdio>
- #include<cstring>
- #include<cmath>
- #include<map>
- #include<queue>
- #include<stack>
- #include<vector>
- #include<set>
- #include<ctype.h>
- #include<algorithm>
- #include<string>
- #define PI acos(-1.0)
- #define maxn 1000
- #define INF 1<<25
- #define mem(a, b) memset(a, b, sizeof(a))
- typedef long long ll;
- using namespace std;
- int main ()
- {
- int a, b;
- while(scanf("%d%d", &a, &b))
- {
- if (a == 0 && b == 0) break;
- int k = b - a;
- int n = (1 + sqrt(5.0)) / 2 * k;
- if (a == n) puts("0");
- else
- {
- puts("1");
- if (a - n == b - n - k) printf("%d %d\n", n, n + k); //两者之间的差值不变
- if (a == 0) puts("0 0"); //如果a等于0了,差值只能为0,即为(0,0)。
- for (int i = 1; i <=b; i++) //枚举a和b之间的差值,可能从1一直到b - 1
- {
- n = (1 + sqrt(5.0)) / 2 * i;
- int tmp = n + i;
- if (n == a) printf("%d %d\n", n, tmp);
- if (tmp == a) printf("%d %d\n",n, tmp);
- if (tmp == b) printf("%d %d\n", n, b);
- }
- }
- }
- return 0;
- }
三:尼姆博奕
- /*
- http://acm.hdu.edu.cn/showproblem.php?pid=2176
- 尼姆博奕 并且求输出方案
- */
- #include<iostream>
- #include<cstdio>
- #include<cstring>
- #include<cmath>
- #include<map>
- #include<queue>
- #include<stack>
- #include<vector>
- #include<set>
- #include<ctype.h>
- #include<algorithm>
- #include<string>
- #define PI acos(-1.0)
- #define maxn 200000
- #define INF 1<<25
- #define mem(a, b) memset(a, b, sizeof(a))
- typedef long long ll;
- using namespace std;
- int sg[maxn+10];
- int main ()
- {
- int m;
- while(scanf("%d", &m) && m)
- {
- int sum = 0;
- for (int i = 0; i < m; i++)
- {
- scanf("%d", sg + i);
- sum ^= sg[i];
- }
- if (!sum) puts("No");
- else
- {
- puts("Yes");
- for (int i = 0; i < m; i++)
- {
- // sum = sg[0] ^ sg[1] ^ sg[2] ^ …… ^sg[m - 1]
- int s = sum ^ sg[i]; // s = sum ^ sg[i] 我们可以知道,异或符合交换律,而两个相同的数异或后为0,而一个数与0异或不改变
- //因此,s相当于除了sg[i]之外的所有堆的个数异或
- if (s < sg[i]) printf("%d %d\n", sg[i], s);//如果s<sg[i]。便可以取走sg[i] - s颗石头,变为s颗。两两异或之后为0
- }
- }
- }
- return 0;
- }