bzoj2017[Usaco2009 Nov]硬币游戏*

bzoj2017[Usaco2009 Nov]硬币游戏

题意:

初始时,一个有N枚硬币的堆栈放在地上,每枚硬币都有一个价值。开始玩游戏时,第一个玩家可以从堆顶拿走一枚或两枚硬币。之后每一轮中,当前的玩家至少拿走一枚硬币,至多拿走对手上一次所拿硬币数量的两倍。当没有硬币可拿时,游戏结束。 两个玩家都希望拿到最多钱数的硬币。求第一个玩家最多能拿多少钱。n≤2000。

题解:

首先有dp方程:f[i][j][0]=max(f[i][k][1]+sum(i-k,i)),1≤k≤min(j*2,i),f[i][j][1]=min(f[i][k][0]),1≤k≤min(j*2,i)。

然而这样会超时。发现所以f[i][k][1]的最大值实际上是f[i][j*2][1]、f[i][j*2-1][1]、f[i][j-1][0]的最大值,f[i][k][0]的最小值也是f[i][j*2][0]、f[i][j*2-1][0]、f[i][j-1][1]的最小值。故可以把dp优化到O(n^2)可过。

代码:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #define inc(i,j,k) for(int i=j;i<=k;i++)
 5 #define maxn 2010
 6 using namespace std;
 7 
 8 inline int read(){
 9     char ch=getchar(); int f=1,x=0;
10     while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
11     while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
12     return f*x;
13 }
14 int a[maxn],sm[maxn],f[maxn][maxn][2],n;
15 int main(){
16     n=read(); inc(i,1,n)a[i]=read(); for(int i=n;i>=1;i--)sm[n-i+1]=sm[n-i]+a[i];
17     inc(i,1,n)
18         inc(j,1,n){
19             f[i][j][0]=j>1?f[i][j-1][0]:0; int k;
20             k=2*j-1; if(k<=i)f[i][j][0]=max(f[i][j][0],f[i-k][k][1]+sm[i]-sm[i-k]);
21             k=2*j;  if(k<=i)f[i][j][0]=max(f[i][j][0],f[i-k][k][1]+sm[i]-sm[i-k]);
22             f[i][j][1]=j>1?f[i][j-1][1]:0x3fffffff;
23             k=2*j-1; if(k<=i)f[i][j][1]=min(f[i][j][1],f[i-k][k][0]);
24             k=2*j; if(k<=i)f[i][j][1]=min(f[i][j][1],f[i-k][k][0]);
25         }
26     printf("%d",f[n][1][0]); return 0;
27 }

 

20160830

posted @ 2016-09-03 22:00  YuanZiming  阅读(272)  评论(0编辑  收藏  举报