Day2

今天讲了博弈论的相关知识,感觉很注重思维和模型积累的是SG函数,应该是需要多做题才可以熟练掌握。

聚聚帮忙列出了几种应该掌握的博弈模型:1、巴什博弈   2、威佐夫博弈    3、斐波那契博弈  4、尼姆博弈   5、阶梯博弈    6\Nim博弈的第二种形式   7、树上博弈  

找到了两篇很不错的关于博弈论知识的博客,并且学习了里面的一些姿势:

1、http://www.cnblogs.com/ECJTUACM-873284962/p/6398385.html

2、https://www.cnblogs.com/ECJTUACM-873284962/p/6921829.html

下来总结今日学习的题目:

A - Playing With Stones

题意:n堆stones,一次最多只能取走一半stones,stone为1时失败,先手是否胜利。

Thinking:

1、打表法找出SG函数的规律。 2、用亦或表示P-N状态。 3、利用多组Nim博弈的结论进行判断。

 1 #include <cstdio>
 2 #include <cstring>
 3 #define LL long long
 4 const int maxn = 1e3+10;
 5 /*
 6 int SG[maxn], S[maxn];
 7 void getSG(int n){
 8     memset(SG, 0, sizeof(SG));
 9     //sg[0]=0;  sg[1]=0;
10     for(int i=2; i<=n; i++){
11         memset(S, 0, sizeof(S));
12         for(int j=1; j*2<=i; j++){
13             S[SG[i-j]] = 1;
14         }
15         for(int j=0; ;j++){
16             if(!S[j]){
17                 SG[i] = j;
18                 break;
19             }
20         }
21     }
22 }
23 int main(){
24     getSG(100);
25     for(int i=1; i<=50; i++){
26         if(i%10==0)    printf("\n");
27         printf("%d ",SG[i]);
28     }
29     return 0;
30 }
31 */
32 LL getsg(LL n){
33     return n%2 ? getsg(n/2) : n/2;
34 }
35 int main(){
36     int T,N;
37     scanf("%d",&T);
38     for(int t=0; t<T; t++){
39         scanf("%d",&N);
40         LL ans = 0;
41         for(int i=0; i<N; i++){
42             LL tmp;
43             scanf("%lld",&tmp);
44             ans ^= getsg(tmp);
45         }
46         if(ans){
47             printf("YES\n");
48         }else{
49             printf("NO\n");
50         }
51     }
52     return 0;
53 }

B - 悼念512汶川大地震遇难同胞——选拔志愿者 

C - Public Sale 

Thinking:

很明显的巴什博弈。

策略:一堆n个石子,最多取m个,最少1个,A先手。 n%(m+1)==0 :B胜; 否则A开始取n-n/(m+1)*(m+1)个石子,之后石子和始终为(m+1),即A胜。

 1 #include <cstdio>
 2 int main(){
 3     int C;
 4     scanf("%d",&C);
 5     for(int c=0; c<C; c++){
 6         int n,m;
 7         scanf("%d%d",&n, &m);
 8         if(n%(m+1) == 0){
 9             printf("Rabbit\n");
10         }else{
11             printf("Grass\n");
12         }
13     }
14     return 0;
15 }
View Code
 1 #include <cstdio>
 2 int main(){
 3     int n,m;
 4     while( scanf("%d%d",&m,&n) != EOF){
 5         if(m%(n+1) == 0){
 6             printf("none\n");
 7         }else if(m <= n){
 8             for(int i=m; i<=n; i++){
 9                 printf("%d%s",i,i==n?"\n":" ");
10             }
11         }else{
12             printf("%d\n",m-(m/(n+1))*(n+1));
13         }
14     }
15     return 0;
16 }
View Code

D - No Gambling 

认真读题就好了

题意:

给定两个N*N由红点和蓝点构成的完全对称的图,两个人轮流连边,每次只能对相邻同颜色两点连一条边且不能穿过对方所连的线,先手对蓝点连边,从左到右获胜,后手对红点连边,从上到下获胜,问谁胜谁负。

1 #include <cstdio>
2 int main(){
3     int n;
4     while(scanf("%d",&n) && (n+1)){
5         printf("I bet on Oregon Maple~\n");
6     }
7     return 0;
8 }
View Code

E - Good Luck in CET-4 Everybody! 

Thinking:

开始产生错误思路:看到2的幂,想到利用数的二进制和位运算来判断,但发现当n=21=1+4+16时答案错误。

正解:sg打表找规律。关于规律为3的倍数先手胜利的思考:这是保证最后的1由先手来取,先手操作时始终保持留给对手的是3的倍数即可。

 1 /*
 2 int f[11];
 3 bool sg[1005];
 4 void init(){
 5     f[0]=1;
 6     for(int i=1; i<=10; i++)    f[i] = f[i-1]*2;
 7     for(int i=1; i<=1000; i++){
 8         for(int j=0; f[j]<=i; j++){
 9             if(!sg[i-f[j]]){
10                 sg[i]=true;
11                 break;
12             }
13         }
14     }
15 }
16 */
17 #include <cstdio>
18 int main(){
19     int n;
20     while(scanf("%d",&n) != EOF){
21         if(n%3==0){
22             printf("Cici\n");
23         }else{
24             printf("Kiki\n");
25         }
26     }
27     return 0;
28 }

F - Coin Game 

题意: 将给定的n个硬币排成一圈,两人轮流取硬币,每次只能取连续的1到k个硬币,问先手胜还是负。

如果能够把游戏分成对称的两部分,那么先手在某一部分里做了某个操作,后手就可以在另一部分里做和它同样的操作。最后肯定是先手先结束他自己的那一部分,后手再结束另一部分,那么后手就胜利了。

 1 #include <cstdio>
 2 int main(){
 3     int T;
 4     scanf("%d",&T);
 5     for(int t=1; t<=T; t++){
 6         int n,k;
 7         scanf("%d%d",&n, &k);
 8         if(k==1 && (n%2==1)){
 9             printf("Case %d: first\n",t);
10         }else if(n <= k){
11             printf("Case %d: first\n",t);
12         }else{
13             printf("Case %d: second\n",t);
14         }
15     }
16     return 0;
17 }

 G - 取石子游戏 

斐波那契博弈。

 1 /*
 2 #include <bits/stdc++.h>
 3 using namespace std;
 4 const int maxn = 1e3+10;
 5 int sg[maxn][maxn];
 6 int dfs(int x, int y){
 7     if(sg[x][y] != -1)    return sg[x][y];
 8     set<int> s;
 9     s.clear();
10     for(int i=1; i<=y; i++){
11         if(x-i >= 0){
12             s.insert(sg[x-i][2*i]);
13         }
14     }//属于这个集合的非负整数 
15     for(int i=0; ;i++){
16         if(s.count(i) == 0){
17             sg[x][y] = i;
18             break;
19         }
20     }//不属于这个集合的最小非负整数 //这里用set()//A题用的是类似与hash的方法 
21     return sg[x][y];
22 }
23 int main()
24 {
25     memset(sg, -1, sizeof(sg));
26     for(int i=1; i<=1000; i++){
27         sg[0][i] = 0;
28         sg[1][i] = 1;
29     }
30     for(int i=2; i<=100; i++){
31         for(int j=1; j<=100; j++){
32             sg[i][j] = dfs(i, j);
33         }
34     }
35     for(int i=1; i<=30; i++){
36         for(int j=1; j<=30; j++){
37             printf("%2d ",sg[i][j]);
38         }
39         cout << endl;
40     }
41     bool flag = true;
42     for(int i=1; i<=100; i++){
43         for(int j=1; j<i; j++){
44             if(sg[i][j]!=0){
45                 flag=false;  break;
46             }
47         }
48         if(flag){
49             cout << i << endl;
50             flag = true;
51         }else{
52             flag = true;
53         }
54     }
55     return 0;
56 }
57 */
58 #include <bits/stdc++.h>
59 using namespace std;
60 #define LL long long
61 const int maxn = 50;
62 LL f[maxn];
63 void fib(){
64     f[1]=1; f[2]=2;
65     for(int i=3; i<maxn; i++){
66         f[i] = f[i-1] + f[i-2];
67     }
68 }
69 int main(){
70     fib();
71     LL n;
72     while(scanf("%lld",&n) != EOF && (n!=0)){
73         if(lower_bound(f, f+50, n)-f>=50 || f[lower_bound(f, f+50, n)-f]!=n){
74             printf("First win\n");
75         }else{
76             printf("Second win\n");
77         }
78     }
79     return 0;
80 }

 

H - Nim or not Nim? 

题意:Alice和Bob轮流取石子,每一次可以从任意一堆中拿走任意个石子,也可将一堆石子分为两个小堆。求必胜策略。

SG函数打表找规律。

 1 #include <cstdio>
 2 #include <cstring>
 3 /*
 4 const int N = 1e6+5;
 5 int vis[N], sg[N];
 6 void getSG(){
 7     memset(sg, 0, sizeof(sg));
 8     sg[0] = 0;
 9     sg[1] = 1;
10     for(int i=1; i<100; i++){
11         memset(vis, 0, sizeof(vis));
12         for(int j=1; j<=i; j++){
13             vis[sg[i-j]] = 1;
14         }//移走任意个石子
15         for(int j=1; j<i; j++){
16             vis[sg[j]^sg[i-j]] = 1;
17         } //将石子分为两堆
18         for(int j=0; ; j++){
19             if(!vis[j]){
20                 sg[i] = j;
21                 break;
22             }
23         }
24         printf("%d %d\n", i, sg[i]);
25     }
26 }
27 */
28 int getsg(int n){
29     if(n%4==3)    return n+1;
30     else if(n%4==0)    return n-1;
31     else    return n;
32 }
33 int main(){
34     int T;
35     scanf("%d", &T);
36     for(int t=0; t<T; t++){
37         int N,ans = 0;
38         scanf("%d", &N);
39         for(int i=0; i<N; i++){
40             int tmp;
41             scanf("%d", &tmp);
42             ans ^= getsg(tmp);
43         }
44         if(ans == 0){
45             printf("Bob\n");
46         }else{
47             printf("Alice\n");
48         }
49     }
50     return 0;
51 }
View Code

 

K - Being a Good Boy in Spring Festival 

题意:有m堆牌,两个人先后取某堆中的任意(不少于一)张牌,最后取完者胜;问先手取胜第一次取牌有多少种取法。

Thinking:

利用亦或的性质,可以看成是自反性吧,然后逆着思考 必胜的情况下 首次操作 可以对哪些堆,可以保证亦或和为0;并且要符合实际,即 所取数量<=已有数量(此题小于也可以过,=应该更严谨吧)

 1 #include <cstdio>
 2 int a[105];
 3 int main(){
 4     int m;
 5     while( scanf("%d",&m)!=EOF &&  m ){
 6         int ans = 0;
 7         for(int i=0; i<m; i++){
 8             scanf("%d",&a[i]);
 9             ans ^= a[i];
10         }
11         if(ans == 0){
12             printf("0\n");
13         }else{
14             int num=0;
15             for(int j=0; j<m; j++){
16                 if((ans^a[j]) <= a[j]){
17                     num++;
18                 }
19             }
20             printf("%d\n",num);
21         }
22     }
23     return 0;
24 }

 L - 取(m堆)石子游戏 

Nim博弈,与K相似。

 1 #include <cstdio>
 2 const int maxm = 200005;
 3 int a[maxm];
 4 int main(){
 5     int m;
 6     while( scanf("%d",&m)!=EOF && m ){
 7         int ans=0;
 8         for(int i=0; i<m; i++){
 9             scanf("%d", &a[i]);
10             ans ^= a[i];
11         }
12         if(ans == 0){
13             printf("No\n");
14         }else{
15             printf("Yes\n");
16             for(int i=0; i<m; i++){
17                 int tmp = ans^a[i];
18                 if(tmp <= a[i]){
19                     printf("%d %d\n", a[i], tmp);
20                 }
21             }
22         }
23     }
24     return 0;
25 }
View Code

M - 取石子游戏 

Thinking:

很直接的威佐夫博弈,由此题可知积累模型的重要性,否则,这个题打表肉眼恐怕很难找到规律。

 1 #include <cstdio>
 2 #include <cmath>
 3 int main(){
 4     int a,b;
 5     while(scanf("%d%d", &a, &b)!=EOF){
 6         if(a > b){
 7             int t=a; a=b; b=t;
 8         }
 9         int i = b-a;
10         b = (int)((sqrt(5.0)+1.0)/2.0*i);
11         if(a==b)    printf("0\n");
12         else    printf("1\n");
13     }
14     return 0;
15 }
View Code

 

转载于:https://www.cnblogs.com/seaupnice/p/9410938.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值