[BZOJ2017][Usaco2009 Nov]硬币游戏

Description

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

Input

第1行:1个整数N

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

Output

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

Sample Input

5
1
3
1
7
2

Sample Output

9

HINT

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

Source

Silver

好神的$dp$。 真的好难想。。

设$f[i][j]$表示还剩$i$个硬币上一个人取了$j$个的最大收益

设$sum[i]$表示硬币价值前缀和

那么我们有状态转移方程$f[i][j]=max(f[i][j],sum[i]-f[i-k][k])(1<=k<=min(i,2*j))$

这样我们得到一个$O(n^3)$的算法,到这应该都还能理解

然而并不能跑过去,我们可以发(参)现(考)一个很吊的优(题)化(解)

我们考虑到$f[i][j]$和$f[i][j-1]$绝大多数状态相同,我们只用再多考虑多出来的两组即可

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #define M 2010
 4 using namespace std;
 5 int n;
 6 int a[M],f[M][M];
 7 int main()
 8 {
 9     scanf("%d",&n);
10     for(int i=n;i>=1;i--) scanf("%d",&a[i]);
11     for(int i=1;i<=n;i++) a[i]+=a[i-1];
12     for(int i=1;i<=n;i++)
13         for(int j=1;j<=n;j++)
14         {
15             f[i][j]=f[i][j-1];
16             int k=j*2;
17             if(i>=k) f[i][j]=max(f[i][j],a[i]-f[i-k][k]);
18             k=j*2-1;
19             if(i>=k) f[i][j]=max(f[i][j],a[i]-f[i-k][k]);
20         }
21     printf("%d",f[n][1]);
22     return 0;
23 }

猜你喜欢

转载自www.cnblogs.com/Slrslr/p/9576989.html
今日推荐