博弈论 题

来源:互联网 发布:算法引论 pdf百度云 编辑:程序博客网 时间:2024/04/29 20:23

博弈的几道模板题,看完知识点后用来练手吧。


解题模型:

   1.把原游戏分解成多个独立的子游戏,则原游戏的SG函数值是它的所有子游戏的SG函数值的异或。

      sg(G)=sg(G1)^sg(G2)^...^sg(Gn)。

   2.分别考虑没一个子游戏,计算其SG值。

     SG值的计算方法:(重点

      1.可选步数为1~m的连续整数,直接取模即可,SG(x) = x % (m+1);

2.可选步数为任意步,SG(x) = x;

3.可选步数为一系列不连续的数,用模板计算。

       模板1:打表

  

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. //f[]:可以取走的石子个数    
  2. //sg[]:0~n的SG函数值    
  3. //hash[]:mex{}    
  4. int f[N],sg[N],hash[N];         
  5. void getSG(int n)    
  6. {    
  7.     int i,j;    
  8.     memset(sg,0,sizeof(sg));    
  9.     for(i=1;i<=n;i++)    
  10.     {    
  11.         memset(hash,0,sizeof(hash));    
  12.         for(j=1;f[j]<=i;j++)    
  13.             hash[sg[i-f[j]]]=1;    
  14.         for(j=0;j<=n;j++)    //求mes{}中未出现的最小的非负整数    
  15.         {    
  16.             if(hash[j]==0)    
  17.             {    
  18.                 sg[i]=j;    
  19.                 break;    
  20.             }    
  21.         }    
  22.     }    
  23. }    

     

  模板二:DFS

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. //注意 S数组要按从小到大排序 SG函数要初始化为-1 对于每个集合只需初始化1遍    
  2. //n是集合s的大小 S[i]是定义的特殊取法规则的数组    
  3. int s[110],sg[10010],n;    
  4. int SG_dfs(int x)    
  5. {    
  6.     int i;    
  7.     if(sg[x]!=-1)    
  8.         return sg[x];    
  9.     bool vis[110];    
  10.     memset(vis,0,sizeof(vis));    
  11.     for(i=0;i<n;i++)    
  12.     {    
  13.         if(x>=s[i])    
  14.         {    
  15.             SG_dfs(x-s[i]);    
  16.             vis[sg[x-s[i]]]=1;    
  17.         }    
  18.     }    
  19.     int e;    
  20.     for(i=0;;i++)    
  21.         if(!vis[i])    
  22.         {    
  23.             e=i;    
  24.             break;    
  25.         }    
  26.     return sg[x]=e;    
  27. }   



一般DFS只在打表解决不了的情况下用,首选打表预处理。


3.计算sg(G)=sg(G1)^sg(G2)^...^sg(Gn),
    sg(G)=0,即P-Position,即先手比败。



LightOJ 1247 Matrix Game

Given an m x n matrix, where m denotes the number of rows and n denotes the number of columns and in each cell a pile of stones is given. For example, let there be a 2 x 3 matrix, and the piles are

2 3 8

5 2 7

That means that in cell(1, 1) there is a pile with 2 stones, in cell(1, 2) there is a pile with 3 stones and so on.

Now Alice and Bob are playing a strange game in this matrix. Alice starts first and they alternate turns. In each turn a player selects a row, and can draw any number of stones from any number of cells in that row. But he/she must draw at least one stone. For example, if Alice chooses the 2nd row in the given matrix, she can pick 2 stones from cell(2, 1), 0 stones from cell (2, 2), 7 stones from cell(2, 3). Or she can pick 5 stones from cell(2, 1), 1 stone from cell(2, 2), 4 stones from cell(2, 3). There are many other ways but she must pick at least one stone from all piles. The player who can't take any stones loses.

Now if both play optimally who will win?

Input

Input starts with an integer T (≤ 100), denoting the number of test cases.

Each case starts with a line containing two integers: m and n (1 ≤ m, n ≤ 50). Each of the next m lines contains n space separated integers that form the matrix. All the integers will be between 0 and 109 (inclusive).

Output

For each case, print the case number and 'Alice' if Alice wins, or 'Bob' otherwise.

Sample Input

2

2 3

2 3 8

5 2 7

2 3

1 2 3

3 2 1

Sample Output

Case 1: Alice

Case 2: Bob


题解:每一行虽有m堆石子,但都可以看成1堆石子。这题就是NIM游戏,m堆是用来唬人的。。。

套用解题模型:

 1.把原游戏分解成n个一堆石子的博弈题,则原游戏的SG函数值是n个子游戏的SG函数值的异或。

      sg(G)=sg(G1)^sg(G2)^...^sg(Gn)。

 2.分别考虑没一个子游戏,计算其SG值。

         可选步数为任意步,SG(x) = x;



#include<iostream>#include<cstdio>using namespace std;int main(){    int t;    int n,m;    long long sum,a,ans;    cin>>t;    for(int kase=1;kase<=t;kase++)    {        scanf("%d%d",&n,&m);        sum=0;        for(int j=0;j<m;j++)        {            scanf("%lld",&a);            sum+=a;        }        ans=sum;        for(int i=1;i<n;i++)        {            sum=0;            for(int j=0;j<m;j++)            {                scanf("%lld",&a);                sum+=a;            }            ans=ans^sum;        }        if(ans==0)        {            printf("Case %d: Bob\n",kase);        }        else            printf("Case %d: Alice\n",kase);    }}


LightOJ 1296Again Stone Game

Again Stone Game
Time Limit:2000MS     Memory Limit:32768KB     64bit IO Format:%lld & %llu
Submit Status Practice LightOJ 1296

Description

Alice and Bob are playing a stone game. Initially there are n piles of stones and each pile contains some stone. Alice stars the game and they alternate moves. In each move, a player has to select any pile and should remove at least one and no more than half stones from that pile. So, for example if a pile contains 10 stones, then a player can take at least 1 and at most 5 stones from that pile. If a pile contains 7 stones; at most 3 stones from that pile can be removed.

Both Alice and Bob play perfectly. The player who cannot make a valid move loses. Now you are given the information of the piles and the number of stones in all the piles, you have to find the player who will win if both play optimally.

Input

Input starts with an integer T (≤ 100), denoting the number of test cases.

Each case starts with a line containing an integer n (1 ≤ n ≤ 1000). The next line contains n space separated integers ranging in [1, 109]. The ith integer in this line denotes the number of stones in the ith pile.

Output

For each case, print the case number and the name of the player who will win the game.

Sample Input

5

1

1

3

10 11 12

5

1 2 3 4 5

2

4 9

3

1 3 9

Sample Output

Case 1: Bob

Case 2: Alice

Case 3: Alice

Case 4: Bob

Case 5: Alice


题解:

 1.把原游戏分解成n个在一堆石子(a[i]颗)中取1~a[i]/2颗的独立的子游戏,则原游戏的SG函数值是它的所有子游戏的SG函数值的异或。

      sg(G)=sg(G1)^sg(G2)^...^sg(Gn)。

2.分别考虑没一个子游戏,计算其SG值。

      可选步数为1~m的连续整数,直接取模即可,SG(x) = x % (m+1);


ps:当时忘了可选步数为1~m的连续整数,每堆石子的SG(x) = x % (m+1)这点;so,打表求sg,然而每堆石子数>1e9,so,找规律,还真能找到规律*^*.

#include<iostream>#include<cstdio>#include<cstring>using namespace std;typedef long long LL;/*const LL N=505;int sgv[N],hs[N];void Init_SG(){    memset(sgv,0,sizeof(0));    for(int i=1;i<=N;i++)    {        memset(hs,0,sizeof(hs));        for(int j=1;j<=i/2;j++)        {            hs[sgv[i-j]]=1;        }        for(int j=0;j<=i/2;j++)        {            if(hs[j]==0)            {                sgv[i]=j;                break;            }        }    }}void test(){    Init_SG();    for(int i=0;i<=500;i++)        if(i&1&&(i-1)%4!=0&&(i-3)%8!=0&&(i-7)%16!=0)        cout<<i<<' '<<sgv[i]<<endl;}*/LL sg(LL n){    if(n&1)    {        LL m=1;        while(m<n)        {            m*=2;            if((n-m+1)%(m*2)==0)                return (n-m+1)/(m*2);        }    }    if(n%2==0)        return n/2;    return 0;}int main(){    //test();    int t;    scanf("%d",&t);    for(int kase=1;kase<=t;kase++)    {        int n;        scanf("%d",&n);        LL a;        scanf("%lld",&a);        LL ans=sg(a);        for(int i=1;i<n;i++)        {            scanf("%lld",&a);            ans^=sg(a);        }        if(ans==0)            printf("Case %d: Bob\n",kase);        else            printf("Case %d: Alice\n",kase);    }}



LightOJ 1253Misere Nim

Misere Nim
Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%lld & %llu
Submit Status Practice LightOJ 1253

Description

Alice and Bob are playing game of Misère Nim. Misère Nim is a game playing on k piles of stones, each pile containing one or more stones. The players alternate turns and in each turn a player can select one of the piles and can remove as many stones from that pile unless the pile is empty. In each turn a player must remove at least one stone from any pile. Alice starts first. The player who removes the last stone loses the game.

Input

Input starts with an integer T (≤ 200), denoting the number of test cases.

Each case starts with a line containing an integer k (1 ≤ k ≤ 100). The next line contains k space separated integers denoting the number of stones in each pile. The number of stones in a pile lies in the range [1, 109].

Output

For each case, print the case number and 'Alice' if Alice wins otherwise print 'Bob'.

Sample Input

3

4

2 3 4 5

5

1 1 2 4 10

1

1

Sample Output

Case 1: Bob

Case 2: Alice

Case 3: Bob



#include<iostream>#include<cstdio>using namespace std;int main(){    int t;    cin>>t;    for(int kase=1;kase<=t;kase++)    {        int k;        cin>>k;        long long a=0,b,cnt=0;        for(int i=0;i<k;i++)        {            scanf("%lld",&b);            if(b>1)                cnt++;            a=a^b;        }        if((!a&&!cnt)||(a&&cnt))            printf("Case %d: Alice\n",kase);        else            printf("Case %d: Bob\n",kase);    }}



LightOJ 1199Partitioning Game

Partitioning Game
Time Limit:4000MS     Memory Limit:32768KB     64bit IO Format:%lld & %llu
Submit Status Practice LightOJ 1199

Description

Alice and Bob are playing a strange game. The rules of the game are:

1.      Initially there are n piles.

2.      A pile is formed by some cells.

3.      Alice starts the game and they alternate turns.

4.      In each tern a player can pick any pile and divide it into two unequal piles.

5.      If a player cannot do so, he/she loses the game.

Now you are given the number of cells in each of the piles, you have to find the winner of the game if both of them play optimally.

Input

Input starts with an integer T (≤ 1000), denoting the number of test cases.

Each case starts with a line containing an integer n (1 ≤ n ≤ 100). The next line contains n integers, where the ith integer denotes the number of cells in the ith pile. You can assume that the number of cells in each pile is between 1 and 10000.

Output

For each case, print the case number and 'Alice' or 'Bob' depending on the winner of the game.

Sample Input

3

1

4

3

1 2 3

1

7

Sample Output

Case 1: Bob

Case 2: Alice

Case 3: Bob


#include<iostream>#include<cstdio>#include<cstring>using namespace std;typedef long long LL;const LL N=1e4+5;int sg[N],hs[N];//sg[x] = mex(sg[1]^sg[x-1],sg[2]^sg[x-2],...,sg[(x-1)/2]^sg[x-(x-1)/2]);void Init_SG(){    memset(sg,0,sizeof(sg));    sg[0]=sg[1]=sg[2]=0;    for(int i=3;i<=N;i++)    {        memset(hs,0,sizeof(hs));        for(int j=1;j<=(i-1)/2;j++)        {            hs[sg[j]^sg[i-j]]=1;        }        for(int j=0;j<=i;j++)        {            if(hs[j]==0)            {                sg[i]=j;                break;            }        }    }}/*void test(){    Init_SG();    for(int i=0;i<=100;i++)        cout<<i<<' '<<sg[i]<<endl;}*/int main(){    //test();    int t;    scanf("%d",&t);    Init_SG();    for(int kase=1;kase<=t;kase++)    {        int n;        scanf("%d",&n);        LL a,ans=0;        for(int i=0;i<n;i++)        {            scanf("%lld",&a);            ans^=sg[a];        }        if(ans==0)            printf("Case %d: Bob\n",kase);        else            printf("Case %d: Alice\n",kase);    }}



LightOJ 1315Game of Hyper Knights

Game of Hyper Knights
Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%lld & %llu
Submit Status Practice LightOJ 1315

Description

A Hyper Knight is like a chess knight except it has some special moves that a regular knight cannot do. Alice and Bob are playing this game (you may wonder why they always play these games!). As always, they both alternate turns, play optimally and Alice starts first. For this game, there are 6 valid moves for a hyper knight, and they are shown in the following figure (circle shows the knight).

They are playing the game in an infinite chessboard where the upper left cell is (0, 0), the cell right to (0, 0) is (0, 1). There are some hyper knights in the board initially and in each turn a player selects a knight and gives a valid knight move as given. And the player who cannot make a valid move loses. Multiple knights can go to the same cell, but exactly one knight should be moved in each turn.

Now you are given the initial knight positions in the board, you have to find the winner of the game.

Input

Input starts with an integer T (≤ 200), denoting the number of test cases.

Each case starts with a line containing an integer n (1 ≤ n ≤ 1000) where n denotes the number of hyper knights. Each of the next n lines contains two integers x y(0 ≤ x, y < 500) denoting the position of a knight.

Output

For each case, print the case number and the name of the winning player.

Sample Input

2

1

1 0

2

2 5

3 5

Sample Output

Case 1: Bob

Case 2: Alice



#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <vector>using namespace std;const int N=1005,L=10;int xx[6]= {-2,-3,-2,-1,-1,1};int yy[6]= {1,-1,-1,-2,-3,-2};int sg[N][N];bool vis[N][N];//int sta[L],top;int calc_sg(int x,int y){    bool tag[L];    if(vis[x][y]) return sg[x][y];    memset(tag,0,sizeof(tag));    vis[x][y]=1;///    for(int i=0; i<6; i++)    {        int tx=x+xx[i];        int ty=y+yy[i];        if(tx>=0 && ty>=0) tag[calc_sg(tx,ty)]=1; //sta[top++]=calc_sg(tx,ty);    }    //vis[x][y]=1;    for(int i=0; i<L; i++)    {        if(tag[i]==0)        {            sg[x][y]=i;            break;        }    }    return sg[x][y];}int main(){    //freopen("cin.txt","r",stdin);    int t,ca=1;    int n;    cin>>t;    while(t--)    {        scanf("%d",&n);        int ans=0;        for(int i=0; i<n; i++)        {            int a,b;            scanf("%d%d",&a,&b);            ans=ans^calc_sg(a,b);        }        printf("Case %d: ",ca++);        if(ans) puts("Alice");        else puts("Bob");    }    return 0;}/*33123 456334 54483 */



LightOJ 1393Crazy Calendar

Crazy Calendar
Time Limit:4000MS     Memory Limit:32768KB     64bit IO Format:%lld & %llu
Submit Status Practice LightOJ 1393

Description

2011 was a crazy year. Many people all over the world proposed on 11-11-11, married on 11-11-11, some even went through surgery only to have 11-11-11 as their child's birth date. How crazy people can be! Don't they see there is a "20" hidden? Then what to do? A very elegant solution came from ARR, a very famous and funny character - why do we need to follow Christian (or some calls it Gregorian) calendar? Why don't we start our own calendar on the day of marriage? And those who like to celebrate their marriage ceremony too frequent, why don't they declare only 1 day per year. In that fashion they can celebrate their anniversary every day. And may be one minute a year or a second or ... Uh.. getting complex. Let's back to the title. From now, we start to have a new calendar system, "Kisu Pari Na". And we hope to update this calendar on every national contest.

The purpose of this calendar is - we all will try our best to learn something new in every year. For this first year let's learn some combinatory. It reminds me of my first year in college. I faced this problem but could not solve this then. But see how easy it is:

 

 

 

 

 

 

 

 

 

 

 

 

Say you start from upper left cell and want to go to lower right cell. The only restriction is you can only move downward or rightward. How many ways are there? How to solve it? Not that difficult. You have to go two times Down and three times Right (whichever way you try) to reach the goal from the starting cell, right? So the answer is number of ways you can arrange two D (represents Down) and three R (represent Right). 2 same characters and 3 same characters, total 5 characters. So it is:

Or =D+RCR. Easy isn't it?

Ok enough with learning. Now back to problem, given a grid and at each cell there are some coins. Inky and Pinky are playing a game getting inspiration from the above problem. At each turn, a player chooses a non empty cell and then removes one or more coins from that cell and put them to the cell exactly right of it or exactly beneath it. A player can't divide the coins and put one part to right and others to down. Note that, for the cells at the right column the player can't move it to more right, and same for the bottom-most row. So a player can't move coins from the lower right cell. The game will finish when no moves are available and the player who moved last will win. Now inky being very modest asked Pinky to move first. Can you say if Pinky will win if both play perfectly?

Input

Input starts with an integer T (≤ 100), denoting the number of test cases.

Each case starts with a line containing two integers R C (1 ≤ R * C ≤ 50000), where R denotes the number of rows and C denotes the number of columns of the grid respectively. Each of the next R lines contains C space separated integers denoting the grid. These integers lie in the range [0, 109].

Output

For every test case, output case number followed by "win" if Pinky can win or "lose".

Sample Input

1

2 2

1 1

1 1

Sample Output

Case 1: lose


#include <iostream>#include <cstdio>#include <cstring>using namespace std;int main(){    int t;    scanf("%d",&t);    int k=1;    while(t--)    {        int n,m,x;        scanf("%d%d",&n,&m);        int sum=0;        for(int i=1;i<=n;i++)        {            for(int j=1;j<=m;j++)            {                scanf("%d",&x);                if(((i+j)%2)!=((m+n)%2))                    sum^=x;            }        }        if(sum==0)             printf("Case %d: lose\n",k++);        else              printf("Case %d: win\n",k++);    }    return 0;}





0 0
原创粉丝点击