JZOJ 1322. 硬币游戏

Description

  FJ的奶牛喜欢玩硬币游戏,所以FJ发明了一个新的硬币游戏。一开始有N(5<=N<=2,000)个硬币堆成一叠,从上往下数第i个硬币有一个整数值C_i(1<=C_i<=100,000)。
  两个玩家轮流从上倒下取硬币,玩家1先取,可以从上面取1个或2个硬币,下一轮的玩家可以取的硬币数量最少为1个,最多为上一个玩家取的数量的2倍,硬币全部取完比赛结束。
  已知玩家2绝顶聪明,会采用最优策略,现在请你帮助玩家1,使得玩家1取得的硬币值的和最大。

Input

  第一行输入N
  第二至N+1行每行输入一个整数C_i

Output

  输出玩家1能获得的最大值。

Sample Input

5
1
3
1
7
2

Sample Output

9

Solution

  • 这题显然是一道博弈题(“绝顶聪明”),但是传统的搜索过不了这么大的极限数据。

  • 观察到目标是求极值,于是我们考虑动态规划。

  • F[i][j] 表示还剩余 i 个硬币、上一次对手选了 j 个硬币的最大获利。

  • 再设 sum[i][j] 表示从 i j 的价值和(可以用前缀和维护)。

  • 那么可得转移方程式:

    F[i][j]=max{sum[ik+1][i]+(sum[1][ik]F[ik][k])}

  • 其中:

    1. sum[ik+1][i] 表示本次自己拿走第 i 枚硬币的钱数和;
    2. sum[1][ik]f[ik][k] 表示在剩下的局面中自己还能拿的钱数和;
    3. 1kmin(2k,i)
  • 于是我们将上式化简,得:

    F[i][j]=max{sum[1][i]F[ik][k]} (1kmin(2k,i))

  • 但然而这样的 DP 是 O(N3) 的,极限数据会时间超限,要想办法优化成 O(N2) 的。

  • 继续观察上式,发现两个相邻状态 F[i][j] F[i][j1] 有很多重复的转移,

  • 代入可以发现只有两个状态是有用的,即:当 k=2j k=2j1 时有意义。

  • 那么只转移两个状态,时间复杂度就是 O(N2) ,成功通过本题。

Code

#include<cstdio>
using namespace std;
const int N=2001;
int sum[N],f[N][N];
inline int read()
{
    int X=0,w=1; char ch=0;
    while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();
    return X*w;
}
inline int max(int x,int y)
{
    return x>y?x:y;
}
int main()
{
    int n=read();
    for(int i=1;i<=n;i++) sum[n-i+1]=read();
    for(int i=1;i<=n;i++) sum[i]+=sum[i-1];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n-i+1;j++)
        {
            /*for(int k=1;k<=2*j && k<=i;k++)
                f[i][j]=max(f[i][j],sum[i]-f[i-k][k]); N^3做法 */
            f[i][j]=f[i][j-1];
            int k=2*j-1;
            if(i>=k) f[i][j]=max(f[i][j],sum[i]-f[i-k][k]);
            if(i>=++k) f[i][j]=max(f[i][j],sum[i]-f[i-k][k]);
        }
    printf("%d",f[n][1]);
    return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值