题解 P5858 【「SWTR-03」Golden Sword】

zumgze

2019-12-28 20:09:25

Solution

$20200214markdown$ ~~可能是数据的问题,此方法总体上跑得比单调队列快。~~ 我们可以叫它 **及时终止的动态规划。** 这个算法在不开优化的情况下大约会运行400ms。 --- 我们首先定义$dp[i][j]$为放入第 *i* 个原料后锅内的原料有 *j* 个的时候的耐久度之和,$a[i]$为依次放入的原料。 当处于$dp[i][j]$的状态的时候,向锅里加入原料$a[i+1]$,想想会发生什么? $j$个原料再放入一个原料是$j+1$个原料,**由于可以取出0~s个原料,所以锅里剩下的原料数的范围是$[j+1-s,j+1]$。** 之后再考虑锅的容量,最少$0$个原料,最多$w$个原料,**所以范围就变成了$[max(0,j+1-s),min(w,j+1)]$。** 所以我们可以用$dp[i][j]$来更新$dp[i+1][k]$。 再考虑初始条件,当还没有开始放原料的时候,耐久度必然是0。即$dp[0][0]=0$。 --- #### 然而单纯这样是过不了的,所以我们还要做出两点优化。 ①由于(这里是单独拿出第一维来说)$dp[i]$的值只用来更新$dp[i+1]$,此时$dp[i-1]$及之前的都已经没有用的,所以也就没有储存的必要。所以我们只需要存储$dp[i]$和$dp[i+1]$,从而降低了空间复杂度。 ② **考虑及时终止。** 当用$dp[i][j]$来更新$dp[i+1][k]$的时候,观察式子$dp[i+1][k]=max(dp[i+1][k],dp[i][j]+k*a[i])$。 --- #### 发现增量只与当前锅内的原料数和刚放进去的原料有关。 当用$dp[i][j]$来更新$dp[i+1][k]$的时候,我们顺序枚举$j$,倒序枚举$k$。 若出现$dp[i][j]+k*a[i] \leq dp[i+1][k]$,那么就不需要枚举$k$了,因为这说明在枚举$dp[i][j]$之前,已经出现过一个更大的值将$dp[i+1][k]$更新过了,从而对于$dp[i+1][k-1]$及之后的每一个,$dp[i][j]$都不可能更大。 代码如下: ```cpp #include<bits/stdc++.h>//算法复杂度O(n*w*w) AC 100points using namespace std; long long a[10000],dp[10000]; inline long long read()//读入优化不多说 { long long x=0; bool f=false; char ch=getchar(); while(ch<'0'||ch>'9') { f|=(ch=='-'); ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } if(f)return -x; else return x; } int main() { long long n,w,s; n=read(); w=read(); s=read(); for(long long i=0;i<n;i++)a[i]=read(); for(long long j=1;j<=w;j++)dp[j]=-9999999999999999ll;//初值负无穷 dp[1]=a[0];//当放入第1个原料时,锅中的原料数只能为1,这就是dp初始条件 for(long long i=1;i<n;i++) { long long help[10000];//此时dp[j]相当于dp[i][j] //此时help[j]相当于dp[i+1][j] for(long long j=1;j<=w;j++)help[j]=-9999999999999999ll;//初值负无穷 for(long long j=1;j<=w&&j<=i;j++)//j<=i是放入第i个原料后最多可以使锅里有i个原料 {//注意这里枚举j一定是正序的 for(long long k=min(w,j+1);k>=1&&k>=j-s+1;k--)//注意这里枚举k一定是倒序的 if(help[k]<dp[j]+k*a[i])help[k]=dp[j]+k*a[i]; else break;//如果help[k]>=dp[j]+k*a[i],说明dp[j]这个解没有之前的解优 } for(long long j=1;j<=w;j++)dp[j]=help[j];//将help数组拷贝到dp数组,为下一个循环做准备 } long long ans=-9999999999999999ll;//初值负无穷 for(long long i=1;i<=w;i++)ans=max(ans,dp[i]);//放入最后1个原料后,枚举此时锅中的原料数,求最大值 printf("%lld",ans); return 0; } ```