• BZOJ 2101 [Usaco2010 Dec]Treasure Chest 藏宝箱:区间dp 博弈【两种表示方法】【压维】


    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2101

    题意:

      共有n枚金币,第i枚金币的价值是w[i]。

      把金币排成一条直线,Bessie和Bonny轮流取金币,看谁取到的钱最多。

      Bessie先取,每次只能取一枚金币,而且只能选择取直线两头的金币,不能取走中间的金币。当所有金币取完之后,游戏就结束了。

      Bessie和Bonny都是非常聪明的,她们会采用最好的办法让自己取到的金币最多。

      请帮助Bessie计算一下,她能拿到多少钱?

    题解:

      区间dp共有两种表示状态的方法:

        (1)dp[i][j]:表示区间[i,j]的答案。

          一般转移为:dp[i][j] = best(dp[i+1][j], dp[i][j-1])

        (2)dp[i][j]:左端点为i,区间长度为j。

          一般转移为:dp[i][j] = best(dp[i][j-1], dp[i+1][j-1])

      显然,第二种是可以压维的。因为dp[i][j]只与dp[...][j-1]有关。

      在此题中,第一种表示会炸空间,所以只能用第二种。

      表示状态:

        dp[i][j] = max wealth

        i:起点为i

        j:区间长度为j

        表示对于当前区间,先手的最大获利。

     

      找出答案:

        ans = dp[1][n]

        表示整个区间。

     

      如何转移:

        对于一个区间,这个区间内的价值总和是一定的。

        那么如果要让自己获利更大,就是要让对方接下来的获利最小。

        自己的获利 = 区间价值总和 - 对方获利

        即:dp[i][j] = sum(i,i+j-1) - min(dp[i][j-1], dp[i+1][j-1])

      边界条件:

        dp[i][1] = w[i]

        只能拿走剩下的一个硬币。

      优化:

        压维。

        前缀和。

    AC Code:

     1 // state expression:
     2 // dp[i][j] = max wealth
     3 // i: start pos
     4 // j: len of present section
     5 //
     6 // find the answer:
     7 // dp[1][n]
     8 //
     9 // transferring:
    10 // dp[i][j] = sum(i,i+j-1) - min(dp[i][j-1], dp[i+1][j-1])
    11 //
    12 // boundary:
    13 // dp[i][1] = w[i]
    14 #include <iostream>
    15 #include <stdio.h>
    16 #include <string.h>
    17 #define MAX_N 5005
    18 
    19 using namespace std;
    20 
    21 int n;
    22 int w[MAX_N];
    23 int dp[MAX_N];
    24 int sum[MAX_N];
    25 
    26 void read()
    27 {
    28     cin>>n;
    29     for(int i=1;i<=n;i++)
    30     {
    31         cin>>w[i];
    32     }
    33 }
    34 
    35 void cal_sum()
    36 {
    37     sum[0]=0;
    38     for(int i=1;i<=n;i++)
    39     {
    40         sum[i]=sum[i-1]+w[i];
    41     }
    42 }
    43 
    44 void solve()
    45 {
    46     cal_sum();
    47     for(int j=1;j<=n;j++)
    48     {
    49         for(int i=1;i<=n;i++)
    50         {
    51             if(j==1) dp[i]=w[i];
    52             else dp[i]=sum[i+j-1]-sum[i-1]-min(dp[i],dp[i+1]);
    53         }
    54     }
    55 }
    56 
    57 void print()
    58 {
    59     cout<<dp[1]<<endl;
    60 }
    61 
    62 int main()
    63 {
    64     read();
    65     solve();
    66     print();
    67 }
  • 相关阅读:
    小能客服
    bootstrap 字体图标
    在线绘图(PS)(海报)
    UI教程
    免费在线设计网站
    测量史上首个易语言工程测量模块
    在ado.net中实现oracle存储过程调用两种方式
    VS一些快捷键
    解决Win10家庭版没有‘本地用户和组’问题
    参照示例搭建一个Quertz + Topshelf的一个作业调度服务(基础)
  • 原文地址:https://www.cnblogs.com/Leohh/p/7629846.html
Copyright © 2020-2023  润新知