zoukankan      html  css  js  c++  java
  • BZOJ 1413: [ZJOI2009]取石子游戏

    Description

    有(n)堆石子,将这(n)堆石子摆成一排。游戏由两个人进行,两人轮流操作,每次操作者都可以从最左或最右的一堆中取出若干颗石子,可以将那一堆全部取掉,但不能不取,不能操作的人就输了。

    Solution

    博弈+区间DP.

    这道题好厉害啊qwq...

    首先一个区间([l,r])在左边或右边添加一个数是个(P)一定仅有一个。
    因为有另一个的话,从另一个可以转移到当前这个或者从这个可以转移到另一个((x=y+Delta)),所以仅存在一个。
    有了这个性质,可以考虑递推。
    设(L[i][j])表示在区间([i,j])左边添加(L[i][j])是(P)态,(R[i][j])表示在区间([i,j])右边添加(R[i][j])是(P)态。
    从([i,j-1])来计算(L[i][j]),为了表示方便令(x=L[i][j-1],y=R[i][j-1],z=a[j]).
    1. 若(y=z)这个区间是必败的,所以(L[i][j]=0)。
    2. 若(x,y<z || z<x,y),那么(L[i][j]=z),因为取走左右一堆的是必败的,所以后手只需要和先手相同即可。
    3. 若(xleqslant z<y),那么(L[i][j]=z+1),以为如果取到(x),后手取到(x+1),若取到(x+1),后手取到(x+2),若小于(x),后手只需要保证相等即可。
    4. 若(y<zleqslant x),跟上边类似(L[i][j]=z-1)。

    这道题主要就是在考虑后手的操作。

    Code

    /**************************************************************
        Problem: 1413
        User: BeiYu
        Language: C++
        Result: Accepted
        Time:204 ms
        Memory:9904 kb
    ****************************************************************/
     
    #include <bits/stdc++.h>
    using namespace std;
     
    const int N = 1050;
     
    inline int in(int x=0,char ch=getchar()) { while(ch>'9' || ch<'0') ch=getchar();
        while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();return x; }
     
    int T,n;
    int a[N];
    int L[N][N],R[N][N];
     
    int main() {
        for(T=in();T--;) {
            n=in();
            for(int i=1;i<=n;i++) a[i]=in(),L[i][i]=R[i][i]=a[i];
            for(int i=n;~i;--i) for(int j=i+1;j<=n;j++) {
                int x=L[i][j-1],y=R[i][j-1],z=a[j];
                if(y==z) L[i][j]=0;
                else {
                    if(z<x && z<y) L[i][j]=z;
                    else if(x<=z && z<y) L[i][j]=z+1;
                    else if(y<z && z<=x) L[i][j]=z-1;
                    else if(x<z && y<z) L[i][j]=z;
                }
                x=R[i+1][j],y=L[i+1][j],z=a[i];
                if(y==z) R[i][j]=0;
                else {
                    if(z<x && z<y) R[i][j]=z;
                    else if(x<=z && z<y) R[i][j]=z+1;
                    else if(y<z && z<=x) R[i][j]=z-1;
                    else if(x<z && y<z) R[i][j]=z;
                }
            }
            printf("%d
    ",(n==1||a[1]!=L[2][n]));
        }return 0;
    }
    

      

  • 相关阅读:
    小米手机做USB电脑摄像头啦,亲测可用,附有详细教程!
    【DIY文章列表标签】dt_gry_list
    Oracle 10g 设置 PL/SQL 远程
    关于硬盘“4K扇区”对齐的查看与设置方法
    oracle数据误操作恢复【flashback闪回操作】
    CENTOS下安装LNMP环境随笔
    深喉咙使用心得(陆续更新ing....)
    CENTOS6.3环境下安装VSFTPD 便于开通FTP功能随笔
    MYSQL/SQL_SERVER/ORACLE三种数据库自动备份方法
    U盘安装 ubuntu 12.04随笔
  • 原文地址:https://www.cnblogs.com/beiyuoi/p/6549313.html
Copyright © 2011-2022 走看看