其他分享
首页 > 其他分享> > P5858 「SWTR-03」Golden Sword

P5858 「SWTR-03」Golden Sword

作者:互联网



 

题目背景

小 E 不幸在一场战斗中失去了他的金宝剑。
题目描述

制造一把金宝剑需要 n 种原料,编号为 1 到 n,编号为 i 的原料的坚固值为 ai​。

炼金是很讲究放入原料的顺序的,因此小 E 必须按照 1 到 n 的顺序依次将这些原料放入炼金锅。

但是,炼金锅的容量非常有限,它最多只能容纳 w 个原料。

所幸的是,每放入一个原料之前,小 E 可以从中取出一些原料,数量不能超过 s 个。

    我们定义第 i 种原料的耐久度为:放入第 i 种原料时锅内的原料总数(包括正在放入的原料) × ai​,则宝剑的耐久度为所有原料的耐久度之和。

小 E 当然想让他的宝剑的耐久度尽可能得大,这样他就可以带着它进行更多的战斗,请求出耐久度的最大值。

注:这里的“放入第 i 种原料时锅内的原料总数包括正在放入锅中的原料,详细信息请见样例。
输入格式

第一行,三个整数 n,w,sn,w,sn,w,s。

第二行,n 个整数 a1,a2,…,an​。
输出格式

一行一个整数,表示耐久度的最大值。

 由于放入物品需要按照顺序放入所以考虑动态规划:

又因为一种材料最大的贡献值只与当它放入时里面已有的物品数量有关,所以dp式子显然

表示放入第i个物品并且里面已经有p个物品时的贡献最大值:

dp[i][j]=max(dp[i-1][k]+a[i]*j,dp[i][j])  (j-1<=k<=j+s-1)

但是显然转移的式子是o(n3)的时间复杂度,显然是承受不了的,于是考虑优化

 发现式子拥有单调性质即当f[i-1][k]>=f[i-1][j]时当j>k时后面的式子不会比前面的式子更优。因为更大的j,k也意味着被弹出队列的可能性更大于是则可以用单调队列以O(1)的时间找出最大值。具体实现操作如下:

1.将dp[i-1][j-1]即当当前情况下的第二维最小的情况推入队列,然后将队列里面一定不是最优解的情况全部从尾部删除

2.在从头部获得最优解的时候需要判断当前的情况的合法性即k<=j+s-1;

Attention:

1.由于max的比较,所以需要在开始动规前预处理dp数组为-inf

 

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=5e3+5;
const int maxm=5e3+5;
const int inf=0x3f3f3f3f;
ll n,m,s;
ll a[maxn],q[maxn];
ll dp[maxn][maxm];
ll pos[maxn];
int main()
{
    scanf("%lld%lld%lld",&n,&m,&s);
    for(ll i=1;i<=n;i++)scanf("%lld",&a[i]);
    for(ll i=0;i<=n;++i)for(ll j=0;j<=m;++j)dp[i][j]=-1008600110086001;
    dp[0][0]=0;
    for(ll i=1;i<=n;i++)
    {
        int l=1,r=1;
        q[l]=dp[i-1][m];
        pos[l]=m;
        for(ll j=m;j>=1;j--)
        {
            while(pos[l]>j+s-1&&l<=r)++l;
            while(q[r]<dp[i-1][j-1]&&l<=r)--r;
            pos[++r]=j-1;
            q[r]=dp[i-1][j-1];
            dp[i][j]=q[l]+a[i]*j;
        }
    }
    ll ans=-inf;
    for(ll i=0;i<=m;i++)ans=max(ans,dp[n][i]);
    printf("%lld",ans);
    return 0;
 } 

 

标签:03,原料,ll,SWTR,P5858,maxn,耐久度,放入,dp
来源: https://www.cnblogs.com/oiqwq/p/14838682.html