ACM 第十二天

Posted weixq351

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ACM 第十二天相关的知识,希望对你有一定的参考价值。

博弈论(巴什博奕,威佐夫博弈,尼姆博弈,斐波那契博弈,SG函数,SG定理)

一.  巴什博奕(Bash Game):

  A和B一块报数,每人每次报最少1个,最多报4个,看谁先报到30。这应该是最古老的关于巴什博奕的游戏了吧。

其实如果知道原理,这游戏一点运气成分都没有,只和先手后手有关,比如第一次报数,A报k个数,那么B报5-k个数,那么B报数之后问题就变为,A和B一块报数,看谁先报到25了,进而变为20,15,10,5,当到5的时候,不管A怎么报数,最后一个数肯定是B报的,可以看出,作为后手的B在个游戏中是不会输的。

那么如果我们要报n个数,每次最少报一个,最多报m个,我们可以找到这么一个整数k和r,使n=k*(m+1)+r,代入上面的例子我们就可以知道,如果r=0,那么先手必败;否则,先手必胜。

 

巴什博奕:只有一堆n个物品,两个人轮流从中取物,规定每次最少取一个,最多取m个,最后取光者为胜。

 1 #include <iostream>
 2 using namespace std;
 3 int main()
 4 {
 5     int n,m;
 6     while(cin>>n>>m)
 7       if(n%(m+1)==0)  cout<<"后手必胜"<<endl;
 8       else cout<<"先手必胜"<<endl;
 9     return 0;
10 }
11  

 

二.  威佐夫博弈(Wythoff Game):

有两堆各若干的物品,两人轮流从其中一堆取至少一件物品,至多不限,或从两堆中同时取相同件物品,规定最后取完者胜利。

直接说结论了,若两堆物品的初始值为(x,y),且x<y,则另z=y-x;

记w=(int)[((sqrt(5)+1)/2)*z  ];

若w=x,则先手必败,否则先手必胜。

 1 #include <cstdio>
 2 #include <cmath>
 3 #include <iostream>
 4 using namespace std;
 5 int main()
 6 {
 7     int n1,n2,temp;
 8     while(cin>>n1>>n2)
 9     {
10         if(n1>n2)  swap(n1,n2);
11         temp=floor((n2-n1)*(1+sqrt(5.0))/2.0);
12         if(temp==n1) cout<<"后手必胜"<<endl;
13         else cout<<"先手必胜"<<endl;
14     }
15     return 0;
16 }
17  
18  

 

三.  尼姆博弈(Nimm Game):

尼姆博弈指的是这样一个博弈游戏:有任意堆物品,每堆物品的个数是任意的,双方轮流从中取物品,每一次只能从一堆物品中取部分或全部物品,最少取一件,取到最后一件物品的人获胜。

结论就是:把每堆物品数全部异或起来,如果得到的值为0,那么先手必败,否则先手必胜。

 1 #include <cstdio>
 2 #include <cmath>
 3 #include <iostream>
 4 using namespace std;
 5 int main()
 6 {
 7     int n,ans,temp;
 8     while(cin>>n)
 9     {
10         temp=0;
11         for(int i=0;i<n;i++)
12         {
13             cin>>ans;
14             temp^=ans;
15         }
16         if(temp==0)  cout<<"后手必胜"<<endl;
17         else cout<<"先手必胜"<<endl;
18     }
19     return 0;
20 }
21  
22  

 

四.  斐波那契博弈:

有一堆物品,两人轮流取物品,先手最少取一个,至多无上限,但不能把物品取完,之后每次取的物品数不能超过上一次取的物品数的二倍且至少为一件,取走最后一件物品的人获胜。

结论是:先手胜当且仅当n不是斐波那契数(n为物品总数)

 1 #include <iostream>  
 2 #include <string.h>  
 3 #include <stdio.h>  
 4 using namespace std;  
 5 const int N = 55;    
 6 int f[N];   
 7 void Init()  
 8 {  
 9     f[0] = f[1] = 1;  
10     for(int i=2;i<N;i++)  
11         f[i] = f[i-1] + f[i-2];  
12 }    
13 int main()  
14 {  
15     Init();  
16     int n;  
17     while(cin>>n)  
18     {  
19         if(n == 0) break;  
20         bool flag = 0;  
21         for(int i=0;i<N;i++)  
22         {  
23             if(f[i] == n)  
24             {  
25                 flag = 1;  
26                 break;  
27             }  
28         }  
29         if(flag) puts("Second win");  
30         else     puts("First win");  
31     }  
32     return 0;  
33 } 

 

五、SG函数,SG定理

必胜点和必败点的概念
       P点:必败点,换而言之,就是谁处于此位置,则在双方操作正确的情况下必败。
       N点:必胜点,处于此情况下,双方操作均正确的情况下必胜。
必胜点和必败点的性质
        1、所有终结点是 必败点 P 。(我们以此为基本前提进行推理,换句话说,我们以此为假设)
        2、从任何必胜点N 操作,至少有一种方式可以进入必败点 P。
        3、无论如何操作,必败点P 都只能进入 必胜点 N。

Sprague-Grundy定理(SG定理):

        游戏和的SG函数等于各个游戏SG函数的Nim和。这样就可以将每一个子游戏分而治之,从而简化了问题。而Bouton定理就是Sprague-Grundy定理在Nim游戏中的直接应用,因为单堆的Nim游戏 SG函数满足 SG(x) = x。对博弈不是很清楚的请参照http://www.cnblogs.com/ECJTUACM-873284962/p/6398385.html进行进一步理解。

SG函数:

        首先定义mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。例如mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。

        对于任意状态 x , 定义 SG(x) = mex(S),其中 S 是 x 后继状态的SG函数值的集合。如 x 有三个后继状态分别为 SG(a),SG(b),SG(c),那么SG(x) = mex{SG(a),SG(b),SG(c)}。 这样 集合S 的终态必然是空集,所以SG函数的终态为 SG(x) = 0,当且仅当 x 为必败点P时。

SG(x)=x

Alice and Bob are playing a special chess game on an n × 20 chessboard. There are several chesses on the chessboard. They can move one chess in one turn. If there are no other chesses on the right adjacent block of the moved chess, move the chess to its right adjacent block. Otherwise, skip over these chesses and move to the right adjacent block of them. Two chesses can’t be placed at one block and no chess can be placed out of the chessboard. When someone can’t move any chess during his/her turn, he/she will lose the game. Alice always take the first turn. Both Alice and Bob will play the game with the best strategy. Alice wants to know if she can win the game.

InputMultiple test cases.

The first line contains an integer T(T100), indicates the number of test cases.

For each test case, the first line contains a single integer n(n1000), the number of lines of chessboard.

Then n lines, the first integer of ith line is m(m20), indicates the number of chesses on the ith line of the chessboard. Then m integers pj(1pj20) followed, the position of each chess.
OutputFor each test case, output one line of “YES” if Alice can win the game, “NO” otherwise.Sample Input
2
1
2 19 20
2
1 19
1 18
Sample Output
NO
YES

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n,m,sg[1<<21],vis[21];
 4 int dfs(int x)
 5 {
 6      memset(vis,0,sizeof(vis));
 7      for(int i = 20;i>=0;i--)
 8      {
 9          if(x & (1<<i))
10          {
11              int temp = x;
12              for(int j = i-1;j>=0;j--)
13              {
14                  if(!(x&(1<<j)))
15                  {
16                      temp ^= (1<<j)^(1<<i);
17                      vis[sg[temp]]=1;
18                      break;
19                  }
20              }
21          }
22      }
23      for(int i = 0;i<=20;i++)
24          if(!vis[i])
25              return i;
26      return 0;
27 }
28 int main()
29 {
30      memset(sg,0,sizeof(sg));
31      for(int i = 0;i<(1<<20);i++)
32          sg[i]=dfs(i);
33      int T;
34      scanf("%d",&T);
35      while(T--)
36      {
37          int n;
38          scanf("%d",&n);
39          int ans = 0;
40          for(int i = 0;i<n;i++)
41          {
42              int res = 0,temp;
43              int q;
44              scanf("%d",&q);
45              while(q--)
46                 scanf("%d",&temp),res|=1<<(20-temp);
47              ans^=sg[res];
48          }
49          if(ans)
50              printf("YES
");
51          else
52              printf("NO
");
53      }
54  
55 }

 

 

参见原博客:https://blog.csdn.net/ac_gibson/article/details/41624623,https://www.cnblogs.com/ECJTUACM-873284962/p/6921829.html

以上是关于ACM 第十二天的主要内容,如果未能解决你的问题,请参考以下文章

第十二天-月考

Python基础总结之异常调试代码第十二天开始(新手可相互督促)

第十二天

第十二天打卡

实习第十二天

基础学习之第十二天(装饰器的进阶)