博弈论 x
——关于博弈论
四道例题带你走进博弈论~
(考虑必败态,必胜态)
Ps:要理解这种思想,首先要明白什么叫必败态。说简单点,必败态就是“在对方使用最优策略时,无论做出什么决策都会导致失败的局面”。其他的局面称为胜态,值得注意的是在胜态下做出错误的决策也有可能导致失败。此类博弈问题的精髓就是让对手永远面对必败态。 必败态和胜态有着如下性质: 1、若面临末状态者为获胜则末状态为胜态否则末状态为必败态。 2、一个局面是胜态的充要条件是该局面进行某种决策后会成为必败态。 3、一个局面是必败态的充要条件是该局面无论进行何种决策均会成为胜态 这三条性质正是博弈树的原理,但博弈树是通过计算每一个局面是胜态还是必败态来解题,这样在局面数很多的情况下是很难做到的,此时,我们可以利用人脑的推演归纳能力找到必败态的共性,就可以比较好的解决此类问题了。
1)
有一堆石子共有N个。A B两个人轮流拿,A先拿。每次最少拿1颗,最多拿K颗,拿到最后1颗石子的人获胜。假设A B都非常聪明,拿石子的过程中不会出现失误。给出N和K,问最后谁能赢得比赛。
例如N = 3,K = 2。无论A如何拿,B都可以拿到最后1颗石子。
Input
第1行:一个数T,表示后面用作输入测试的数的数量。(1 <= T <= 10000) 第2 - T + 1行:每行2个数N,K。中间用空格分隔。(1 <= N,K <= 10^9)
Output
共T行,如果A获胜输出A,如果B获胜输出B。
Input示例
4 3 2 4 2 7 3 8 3
Output示例
B
A
A
B
思路:
因为只有拿到最后一块石子才能够胜利,所以如果考虑n是否能够被k+1整除
Eg:若n被整除(Eg中设k<n<2*k)
A若首先取k块,还剩下一块,B拿走,B胜利
首先取1块,那么也刚好能被B取走,B胜利
其余情况类似,所以我们只需判断n是否能够被k+1整除即可
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> using namespace std; int n,k,t; int main() { scanf("%d",&t); while(t--) { scanf("%d%d",&n,&k); if(n<k)///此时需要特判n是否比k大 {///如果小于,A获胜(可以自己试试) printf("A\n"); continue; } else { if(n%(k+1)==0) { printf("B\n"); continue; } else { printf("A\n"); continue; } } } return 0; }
有一堆石子共有N个。A B两个人轮流拿,A先拿。每次只能拿1,3,4颗,拿到最后1颗石子的人获胜。假设A B都非常聪明,拿石子的过程中不会出现失误。给出N,问最后谁能赢得比赛。
例如N = 2。A只能拿1颗,所以B可以拿到最后1颗石子。
Input
第1行:一个数T,表示后面用作输入测试的数的数量。(1 <= T <= 10000) 第2 - T + 1行:每行1个数N。(1 <= N <= 10^9)
Output
共T行,如果A获胜输出A,如果B获胜输出B。
Input示例
3 2 3 4
Output示例
B A A
思路:
通过枚举得出循环
ABAAAAB(7位)
这个序列,所以只要判断是否%7==2或0即可
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> using namespace std; int n,k,t; int main() { scanf("%d",&t); while(t--) { scanf("%d",&n); if(n%7==2 || n%7==0)///规律 { printf("B\n"); continue; } else { printf("A\n"); continue; } } return 0; }
3)
有一堆石子共有N个。A B两个人轮流拿,A先拿。每次拿的数量只能是2的正整数次幂,比如(1,2,4,8,16....),拿到最后1颗石子的人获胜。假设A B都非常聪明,拿石子的过程中不会出现失误。给出N,问最后谁能赢得比赛。
例如N = 3。A只能拿1颗或2颗,所以B可以拿到最后1颗石子。(输入的N可能为大数)
Input
第1行:一个数T,表示后面用作输入测试的数的数量。(1 <= T <= 1000) 第2 - T + 1行:每行1个数N。(1 <= N <= 10^1000)
Output
共T行,如果A获胜输出A,如果B获胜输出B。
Input示例
3 2 3 4
Output示例
A B A
思路:
同找规律得出 AAB 序列3个一循环,所以只要判断是否能够被3整除即可.
但是因为数据范围太大,所以需要加些处理,利用3的倍数小性质
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> using namespace std; long long t,wsum; string n; int main() { scanf("%lld",&t); int len; for(int i=1;i<=t;i++) { cin>>n; wsum=0;///清零!!! len=n.length(); for(int j=0;j<len;j++) wsum+=n[j]-'0'; if(wsum%3==0) { printf("B\n"); continue; } else { printf("A\n"); continue; } } return 0; }
4)
有一堆石子共有N个。A B两个人轮流拿,A先拿。每次拿的数量最少1个,最多不超过对手上一次拿的数量的2倍(A第1次拿时要求不能全拿走)。拿到最后1颗石子的人获胜。假设A B都非常聪明,拿石子的过程中不会出现失误。给出N,问最后谁能赢得比赛。
例如N = 3。A只能拿1颗或2颗,所以B可以拿到最后1颗石子。
Input
第1行:一个数T,表示后面用作输入测试的数的数量。(1 <= T <= 1000) 第2 - T + 1行:每行1个数N。(1 <= N <= 10^9)
Output
共T行,如果A获胜输出A,如果B获胜输出B。
Input示例
3 2 3 4
Output示例
B B A
思路:
找规律得当n为斐波那契数列时A必败,所以预处理一下f数组存放斐波那契数列,判断是否出现过,然后弹出条件是,当当前枚举到的数大于输入的数时,跳出并输出A胜利.
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> using namespace std; const int M = 1233; int t; int f[M]; int main() { f[0]=f[1]=1; for(int i=2;i<=M;i++) f[i]=f[i-1]+f[i-2]; scanf("%d",&t); bool flag; int q; while(t--) { flag=false;///清除标记 scanf("%d",&q); for(int i=1;i<=q;i++) { if(f[i]==q) { printf("B\n"); break; } else if(f[i]>q) { flag=true; break; } } if(flag) printf("A\n"); } return 0; }
End.