zoukankan      html  css  js  c++  java
  • BZOJ2017[USACO 2009 Nov Silver 1.A Coin Game]——DP+博弈论

    题目描述

    农夫约翰的奶牛喜欢玩硬币游戏,因此他发明了一种称为“Xoinc”的两人硬币游戏。 初始时,一个有N(5 <= N <= 2,000)枚硬币的堆栈放在地上,从堆顶数起的第I枚硬币的币值为C_i (1 <= C_i <= 100,000)。 开始玩游戏时,第一个玩家可以从堆顶拿走一枚或两枚硬币。如果第一个玩家只拿走堆顶的一枚硬币,那么第二个玩家可以拿走随后的一枚或两枚硬币。如果第一个玩家拿走两枚硬币,则第二个玩家可以拿走1,2,3,或4枚硬币。在每一轮中,当前的玩家至少拿走一枚硬币,至多拿走对手上一次所拿硬币数量的两倍。当没有硬币可拿时,游戏结束。 两个玩家都希望拿到最多钱数的硬币。请问,当游戏结束时,第一个玩家最多能拿多少钱呢?

    输入

    第1行:1个整数N

    第2..N+1行:第i+1行包含1个整数C_i

    输出

    第1行:1个整数表示第1个玩家能拿走的最大钱数。

    样例输入

    5
    1
    3
    1
    7
    2

    样例输出

    9

    提示

    样例说明:第1个玩家先取走第1枚,第2个玩家取第2枚;第1个取走第3,4两枚,第2个玩家取走最后1枚。

    不要看到博弈论就心里打怵,其实只是用到一些简单的博弈思想qwq。这道题主要还是dp,那么dp状态怎么描述呢?首先不能确定最后取硬币的是先手的人还是后手的人,所以可以考虑把状态倒着推,最终答案用先手的人第一次取的状态表示。那么就可以把状态描述成f[i][j]表示上一个人取了j个,还剩下i个,当前人在后续步骤中(包括当前这次取的)能获得的最大值,s[i]表示最后i个的价值和(就是后缀和)。假设当前人是第一个人,他取了k个(k<=2*j),第二个人之后能获得的最大值就是f[i-k][k],那么想让第一个人这次及之后能获得最大值,就要让f[i-k][k]尽量小,那么只要枚举k,用s[i]-min{f[i-k][k]}就是第一个人的最大值。但这样枚举k一定会TLE,不过可以发现f[i][j]和f[i][j-1]之间只差了f[i-2*j][2*j]和f[i-2*j+1][2*j-1]两个状态,所以可以把f[i][j]先赋成f[i][j-1]再与这两个状态比较一下就行了,但比较前要先判断i和这两个数(2*j和2*j-1)之间大小关系。但最后答案是什么呢?我们并不知道先手的人第一次取了1个还是2个,那么可以假设前面有一个第0号硬币被第二个人取走了,这样最终结果就是f[n][1].虽然状态是倒着推,但为了方便可以倒着读入正着转移.

    最后附上代码.

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    int n;
    int a[2010];
    int s[2010];
    int f[2010][2010];
    int main()
    {
        scanf("%d",&n);
        for(int i=n;i>=1;i--)
        {
            scanf("%d",&a[i]);
        }
        for(int i=1;i<=n;i++)
        {
            s[i]=s[i-1]+a[i];
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                f[i][j]=f[i][j-1];
                if(2*j-1<=i)
                {
                    f[i][j]=max(f[i][j],s[i]-f[i-2*j+1][2*j-1]);
                }
                if(2*j<=i)
                {
                    f[i][j]=max(f[i][j],s[i]-f[i-2*j][2*j]);
                }
            }
        }
        printf("%d",f[n][1]);
    }
    

      

  • 相关阅读:
    Pycharm的使用秘籍
    python 装饰器
    python继承和重写
    python中一切皆对象及__init__.py文件的使用
    python的参数类型与时序解包
    Jmeter使用计数器生成变量
    python unittest套件加载用例时,出现No tests were found,Empty test suite
    Python 执行tail文件并操作
    go 函数和流程控制
    go 数据类型和操作符
  • 原文地址:https://www.cnblogs.com/Khada-Jhin/p/9197069.html
Copyright © 2011-2022 走看看