博弈问题方法单-----找规律篇

1、题目:

[vijos1196] 吃糖果游戏

题解:

一开始看看感觉还可以用【科学的】二维SG,一看数据,10000位,exm?
于是我先写了个SG函数,敲进去几个数据
啊我好像看到规律了!—> AC

嘛这样打表是不行的,我们还是看个正解吧
当游戏状态属于前者时,Matrix67可以把糖果数被5除余1、4或正好除尽的那一堆分成糖果数被5除余数都是2或3的两堆(他总能做到这一点)
而对方不得不把其中一堆糖果又分出新的糖果数被5除余1、4或正好除尽的一堆留给Matrix67操作。
这样逼着对方总是面临必败的状态,使得最后对方不得不把2个糖果或者3个糖果分成两堆,
从而使Matrix67赢得游戏。
反过来,当Matrix67面临两堆糖果的数目被5除余数都是2或3的状态时,Shadow总可以取胜。
所以就是如果两堆的个数的个位数都是2 3 7 8的一个的话
就是Shadow取胜,不然就是Matrix

代码:

暴力【表】

#include 
#include 
#include 
using namespace std;
int sg[1005][1005],n,m;
int get_sg(int x,int y)
{
    if (x>y) swap(x,y);
    if (sg[x][y]!=-1) return sg[x][y];
    if (x==1 || y==1) {sg[x][y]=1; return 1;}
    bool ext[1005];memset(ext,0,sizeof(ext));
    for (int i=1;i+i<=y;i++)
      ext[get_sg(i,y-i)]=1;
    for (int i=1;i+i<=x;i++)
      ext[get_sg(i,x-i)]=1;
    for (int i=0;;i++)
      if (!ext[i]){sg[x][y]=i;break;}
    return sg[x][y];
}
int main()
{
    memset(sg,-1,sizeof(sg));
    while (1)
    {
        scanf("%d%d",&n,&m);
        if (get_sg(n,m)) printf("先手\n");else printf("后手\n");
    }
}

AC

#include 
#include 
using namespace std;
char x[10005],y[10005];
int main()
{
    for (int i=1;i<=10;i++)
    {
        scanf("%s%s",x,y);
        int l1=strlen(x)-1,l2=strlen(y)-1;
        if ((x[l1]=='2'||x[l1]=='3'||x[l1]=='7'||x[l1]=='8')&&(y[l2]=='2'||y[l2]=='3'||y[l2]=='7'||y[l2]=='8')) 
          printf("Shadow\n");else printf("Matrix67\n");
    }
}

2、题目

[POJ2505] A multiplication game

题解:

首先想到打表,但这什么鬼啊我不会啊!那就硬找规律?
容易发现
[1,9]Stan
[10,18]Ollie
[19,162]Stan

我们可以这样极限考虑,要是Stan知道ta会赢,那每回合会选最大的9,Ollie知道自己乘上也没什么用,给ta个最小的2,然后Stan选个9就能达到自己的目标,Ollie要是赢的话是同样的
然后你随便试试19-162之间的数就发现Ollie总是不能赢,就可以发现规律了
1~9*1 Stan
9*1+1~9*2 Ollie
9*2+1~9*2*9 Stan
那么我们往下推测
9*2*9+1~9*2*9*2 Ollie
规律get?
其实还有一个问题是这个数字很大,但是可以用double读入

代码:

#include 
#include 
using namespace std;
double n;
int main()
{
    while (~scanf("%lf",&n))
    {
        while (1)
        {
            if (n<=9) {printf("Stan wins.\n");break;}
            if (n<=18) {printf("Ollie wins.\n");break;} 
            n/=18;
        }
    }
}

3、题目:

[HDU3032] Nim or not Nim?

题意:

Alice和Bob轮流取石子,每一次可以从任意一堆中拿走任意个石子,也可以将一堆石子分为两个小堆。先拿完者获胜。

题解:

这个问题可以用SG函数来解决。首先,操作①其实和Nim游戏没什么区别,对于一个石子数为k的点来说,后继可以为0…k-1。而操作②实际上是把一个游戏分成了两个游戏。根据游戏的和的概念,这两个游戏的和应该为两个子游戏的SG函数值的异或。
比如说,状态3的后继有:0、1、2、(1,2),他们的SG值分别为0、1、2、3,所以sg(3) = 4
但是一看数据范围2^31 - 1。。。然后打了个表,规律get?

代码:

#include 
#include 
using namespace std;
int sg[10005];
int get_sg(int x)
{
    if (sg[x]!=-1) return sg[x];
    bool ext[10005];memset(ext,0,sizeof(ext));
    for (int i=x-1;i>=0;i--)
      ext[get_sg(i)]=1;
    for (int i=1;i+i<=x;i++)
      ext[get_sg(i)^get_sg(x-i)]=1;
    for (int i=0;;i++)
      if (!ext[i]) {sg[x]=i; break;}
    return sg[x];
}
int find_sg(int x)
{
    if (x%4==3) return x+1;
    if (x%4==0) return x-1;
    return x;
}
int main()
{
    //表 
/* memset(sg,-1,sizeof(sg)); for (int i=1;i<=50;i++) printf("sg[%d]=%d\n",i,get_sg(i));*/
    int T,n;
    scanf("%d",&T);
    while (T--)
    {
        int k=0,a;
        scanf("%d",&n);
        for (int i=1;i<=n;i++) 
        {
            scanf("%d",&a);
            k^=find_sg(a);
        }
        if (k) printf("Alice\n");else printf("Bob\n");
    }
}

4、题目:

[BZOJ2463] [中山市选2009] 谁能赢呢?

题解:

这题什么鬼啊,我为什么连暴力都不会啊?
然后画了几个格子试一试,发现早晚他们都会把整个棋盘走一遍,那不就是看这个棋盘格子的奇偶吗。。
再退一步,这不就是看行数的奇偶吗。。
AC。

代码:

#include 
using namespace std;
int main()
{
    int n;
    while (scanf("%d",&n) && n)
    {
        if (n%2) printf("Bob\n");
        else printf("Alice\n");
    }
}

你可能感兴趣的:(博弈)