博弈论

推荐博客:

常见的几种组合博弈:http://www.cnblogs.com/ECJTUACM-873284962/p/6398385.html

SG函数与SG定理:https://www.cnblogs.com/ECJTUACM-873284962/p/6921829.html

SG函数:https://blog.csdn.net/kamisama123/article/details/77649118

例题:

1.hdoj1846 Brave Game

传送:http://acm.hdu.edu.cn/showproblem.php?pid=1846

题意:一堆石子,两个人轮流取,要求每次取的个数在$(1-m)$内。问谁获胜。

分析:巴什博弈模板。

代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int main(){
 4     int t,n,m; scanf("%d",&t);
 5     while (t--){
 6         scanf("%d%d",&n,&m);
 7         if (n%(m+1)==0) printf("second\n"); else printf("first\n");
 8     }
 9     return 0;
10 }
hdoj1846

2.hdoj2149 Public Sale

传送:http://acm.hdu.edu.cn/showproblem.php?pid=2149

题意:物品初始价值为0,两个人轮流加价,范围为$1-m$,当价值$\ge n$时,获胜。问先手第一次加价多少时,一定获胜。

分析:巴什博弈。保证先手必胜的状态下输出先手应该选择的数值。注意$n \le m$。

代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int main(){
 4     int n,m;
 5     while (~scanf("%d%d",&n,&m)){
 6         if (m>=n){
 7             for (int i=n;i<m;i++) printf("%d ",i);
 8             printf("%d\n",m);
 9             continue;
10         }
11         if (n%(m+1)==0){
12             printf("none\n");
13             continue;
14         }
15         printf("%d\n",n%(m+1));
16     }
17     return 0;
18 }
hdoj2149

3.hdoj2188 选拔志愿者

传送:http://acm.hdu.edu.cn/showproblem.php?pid=2188

题意:物品初始价值为0,两个人轮流加价,范围为$1-m$,当价值$\ge n$时,获胜。问谁获胜。

分析:巴什博弈。

代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int main(){
 4     int t,n,m; cin >> t;
 5     while (t--){
 6         cin >> n >> m;
 7         if (n%(m+1)==0) cout << "Rabbit\n";
 8         else cout << "Grass\n";
 9     }
10     return 0;
11 }
hdoj2188

4.hdoj4767 Stone

传送:http://acm.hdu.edu.cn/showproblem.php?pid=4764

题意:两个人轮流写数,要求后一个数比前一个数大,且范围在$1-m$,第一个写下$\geq n$的人输。

分析:巴什博弈。

代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int main(){
 4     int n,m;
 5     while (cin >> n >> m){
 6         if (n+m==0) break;
 7         if ((n-1)%(m+1)==0) cout << "Jiang\n";
 8         else cout << "Tang\n";
 9     }
10     return 0;
11 }
hdoj4767

5.hdoj 2147 kiki's game

传送:http://acm.hdu.edu.cn/showproblem.php?pid=2147

题意:有一个棋盘,两个人轮流操作,每次可以走左,下,左下三个方向,不可操作的人输。问先手的输赢。

分析:用必败,必胜态分析。

代码:

hdoj2147

 6.hdoj2516 取石子游戏

传送:http://acm.hdu.edu.cn/showproblem.php?pid=2516

题意:有一堆石子,两人轮流取,先手第1次可以取任意多个,但不能全部取完。以后每次取的石子数不能超过上次取子数的2倍。取完的人获胜。

分析:斐波那契博弈。https://blog.csdn.net/acm_cxlove/article/details/7835016

代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 ll fib[50];
 5 int main(){
 6     fib[1]=1; fib[2]=2;
 7     for (int i=3;i<50;i++) fib[i]=fib[i-1]+fib[i-2];
 8     ll n;
 9     while (cin >> n && n){
10         int f=1;
11         for (int i=1;i<50;i++)
12             if (fib[i]==n){
13                 printf("Second win\n");
14                 f=0;
15             }
16         if (f) printf("First win\n");
17     }
18     return 0;
19 }
hdoj2516

 7.hdoj1527 取石子游戏

传送:http://acm.hdu.edu.cn/showproblem.php?pid=1527

题意:有两堆石子,两个人轮流取,每次可以在一堆中取任意个,也可以在两堆中取同样多个。取完为胜,问先手的输赢。

分析:威佐夫博弈模板。

代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int main(){
 4     int a,b;
 5     while (~scanf("%d%d",&a,&b)){
 6         if (a>b) swap(a,b);
 7         double k=(sqrt(5.0)-1.0)/2.0;
 8         int j=a*k;
 9         if (a!=j*(int)(k+1)) j++;
10         if (a+j==b) cout << "0\n"; else cout << "1\n";
11     }
12 } 
hdoj1527

 8.hdoj2177 取(2堆)石子游戏

传送:http://acm.hdu.edu.cn/showproblem.php?pid=2177

题意:有两堆石子,两个人轮流取,每次可以在一堆中取任意个,也可以在两堆中取同样多个。取完为胜,问若先手获胜第一次需要取多少。

分析:威佐夫博弈。输出第一次的方案,性质:对于每一个自然数都属于一个奇异局势。一对自然数的差值对应一个奇异局势。

代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int main(){
 4     int n,m;  double tmp=(sqrt(5)+1)/2.0;
 5     while (~scanf("%d%d",&n,&m)){
 6         if (n+m==0) break;
 7         if (n>m) swap(n,m);
 8         int b=(m-n)*tmp;
 9         if (n==b) printf("0\n");
10         else{
11             printf("1\n");
12             if (b<=n) printf("%d %d\n",b,b+m-n);
13             if (n==0 || m==0) printf("0 0\n");
14             for (int i=1;i<=m;i++){
15                 b=i*tmp;
16                 if (b==tmp) continue;
17                 if (b==n && b+i<=m) printf("%d %d\n",b,b+i);
18                 else if (b+i==n && b<=m) printf("%d %d\n",b,b+i);
19                 else if (b<=n && b+i==m) printf("%d %d\n",b,b+i);
20                 else if (b+i<=n && b==m) printf("%d %d\n",b,b+i);
21             }
22         }
23     }
24     return 0;
25 } 
hdoj2177

9.hdoj1850 Being a Good Boy in Spring Festival

传送:http://acm.hdu.edu.cn/showproblem.php?pid=1850

题意:n堆石子,每次可以取一堆中的任意个,问先手获胜的前提下第一次取的方案数有多少。

分析:nim博弈。

代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int a[150];
 4 int main(){
 5     int n;
 6     while (~scanf("%d",&n)){
 7         if (n==0) break;
 8         int flag=0;
 9         for (int i=0;i<n;i++){
10             cin >> a[i];
11             flag^=a[i];
12         }
13         int ans=0;
14         for (int i=0;i<n;i++){
15             int tmp=flag^a[i];
16             if (tmp<a[i]) ans++;
17         }
18         printf("%d\n",ans);
19     }
20     return 0;
21 } 
hdoj1850

10.hdoj1847 Good Luck in CET-4 Everybody!

传送:http://acm.hdu.edu.cn/showproblem.php?pid=1847

题意:一堆石子,两个人轮流取,每次可以取2的幂次方个。问先手是否可以获胜。

分析:SG打表。

代码:

 1 #include<iostream> 
 2 using namespace std; 
 3 int main() { 
 4     int n; 
 5     while(scanf("%d",&n)==1) { 
 6         if(n%3==0) 
 7             cout<<"Cici"<<endl; 
 8         else 
 9             cout<<"Kiki"<<endl; 
10     } 
11     return 0; 
12 } 
hdoj187

11.hdoj1848 Fibonacci again and again

传送:http://acm.hdu.edu.cn/showproblem.php?pid=1848

题意:有三堆石子,两个人轮流取,每次要求取的个数为斐波那契值。问谁会赢。

分析:三堆石子的nim游戏。用SG函数处理,最后取异或和即好。

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1010;
int f[maxn],sg[maxn],S[maxn];
void init(){
    f[1]=1; f[2]=2;
    int tot=2;
    for (int i=3;i;i++){
        tot++;
        f[i]=f[i-1]+f[i-2];
        if (f[i]>maxn) break;
    }
    //SG
    memset(sg,0,sizeof(sg));
    for (int i=1;i<maxn;i++){
        memset(S,0,sizeof(S)); //标记后继状态 
        for (int j=1;f[j]<=i && j<tot;j++)
            S[sg[i-f[j]]]=1;
        for (int j=0;;j++)
            if (!S[j]){sg[i]=j;break;}
    }
    //for (int i=0;i<=20;i++) cout << sg[i] << endl;
}
int main(){
    int n,m,k;
    init();
    while (~scanf("%d%d%d",&n,&m,&k)){
        if (n+m+k==0) break;
        if (sg[n]^sg[m]^sg[k]) printf("Fibo\n");
        else printf("Nacci\n"); 
    }
    return 0;
} 
hdoj1848

 

posted @ 2019-04-10 16:10  Changer-qyz  阅读(176)  评论(0编辑  收藏  举报