zoukankan      html  css  js  c++  java
  • 【BZOJ1413】取石子游戏(博弈,区间DP)

    题意:在研究过Nim游戏及各种变种之后,Orez又发现了一种全新的取石子游戏,这个游戏是这样的:

    有n堆石子,将这n堆石子摆成一排。游戏由两个人进行,两人轮流操作,每次操作者都可以从最左或最右的一堆中取出若干颗石子,

    可以将那一堆全部取掉,但不能不取,不能操作的人就输了。

    Orez问:对于任意给出一个初始一个局面,是否存在先手必胜策略。

    T≤10 n≤1000 每堆的石子数目≤1e9

    思路:From http://www.cnblogs.com/zcwwzdjn/archive/2012/05/26/2519685.html

    在尝试SG函数, 区间DP无果后, 在Discuss的诱导下注意到对于一段区间[L, R], 若L+1到R的石子数固定, 那么使得在这段区间上先手必败的a[L]有且仅有一个.

    这个性质灰常给力啊. 于是可以YY一个状态出来. 设left[i][j]表示, 在[i, j]区间的左边加上left[i][j]这个数后先手必败, right[i][j]的定义类似. 那么最后我们只用看left[2][n]是否等于a[1]就可以了.

    接着我们想办法来算left[i][j]. right[i][j]可以类似的求出.

    设L = left[i][j - 1], R = right[i][j - 1], X = a[j]. 通过下面的分析我们可以发现left[i][j]只和L, R, X三个数有关.

    首先, 最容易想到的是, R = X的情况, 这时[i, j]这段区间已经先手必败, 那么left[i][j] = 0.

    接着, 我们可以发选当X < L且X < R时, left[i][j] = X. 在这种局面下, 若先手在一侧取走一些石子, 那么后手在另外一边取走相同数量的石子就可以了.

    然后我们根据L和R的关系分类讨论一下.

    若L > R, 我们考虑R < x <= L的情况, 这时left[i][j] = X - 1. X - 1 = R时是很轻松的, 因为先手不能把右侧石堆取到R, 所以后手保证每次取之后两堆石子相同就可以了. 当X - 1 > R时, 若先手把左边取到R, 那么后手把右边取到R + 1就可以了; 若先手取到R + 1, 那么后手取到R + 2; 以此类推.

    若L < R, 我们考虑L <= X < R的情况, 这时left[i][j] = X + 1. 这个和上面类似.

    最后的一种情况, x > L且x > R. 其实left[i][j] = X. 若L = R, 没啥说的; 若L和R不等, 我们不妨设L > R, 这时若先手把右边取到L, 那么后手需要把左边取到L - 1, 这时如果先手跟着后手走, 那么后手一颗一颗石子取就赢了; 若先手把左边取到R, 那么后手需要把右边取到R + 1, 这种情况似乎一定成立, 因为后手不会主动走到R + 1, 除非对方走到了R.

    反正这个分析无比蛋疼...首先状态的定义非常奇葩, 具有一定的启发性(因为这题灰常隐蔽的一个性质)...然后分情况讨论无比痛苦...考场上还是找规律吧...

    分类讨论的核心在于要找出后手的必胜策略.

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 #include<algorithm>
     5 #include<cmath>
     6 typedef long long ll;
     7 using namespace std;
     8 #define N   1100
     9 #define oo  10000000
    10 #define MOD 1000000007
    11 
    12 int l[N][N],r[N][N],a[N];
    13 
    14 int main()
    15 { 
    16     int cas;
    17     scanf("%d",&cas);
    18     while(cas--)
    19     {
    20         int n;
    21         scanf("%d",&n);
    22         for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    23         for(int i=1;i<=n;i++) l[i][i]=r[i][i]=a[i];
    24         for(int len=2;len<=n;len++)
    25          for(int i=1;i<=n-len+1;i++)
    26          {
    27              int j=i+len-1;
    28             int p=l[i][j-1];
    29             int q=r[i][j-1];
    30             int x=a[j];
    31             if(x==q) l[i][j]=0;
    32              else if((x<p&&x<q)||(x>p&&x>q)) l[i][j]=x;
    33               else if(p<q) l[i][j]=x+1;
    34                else l[i][j]=x-1;
    35             p=r[i+1][j];
    36             q=l[i+1][j];
    37             x=a[i];
    38             if(x==q) r[i][j]=0;
    39              else if((x<p&&x<q)||(x>p&&x>q)) r[i][j]=x;
    40               else if(p<q) r[i][j]=x+1;
    41                else r[i][j]=x-1;
    42         }
    43         if(n==1) printf("1
    ");
    44          else printf("%d
    ",(r[1][n-1]!=a[n]));
    45     }
    46     return 0;
    47 }
    48     
  • 相关阅读:
    Turtlebot-导航
    Turtlebot-创建地图-Gmapping-Kinect
    Gflags
    Linux Driver 入门
    Linux Kernel 入门
    Linux Driver 入门
    Linux Driver 入门
    Linux Driver 入门
    Win10 复制文件路径
    vs2010 nuget 基础连接已经关闭:发送时发生错误
  • 原文地址:https://www.cnblogs.com/myx12345/p/9958199.html
Copyright © 2011-2022 走看看