洛谷 P5858 「SWTR-03」Golden Sword(单调队列优化dp)

传送门


解题思路

很显然是dp(因为标签是dp)

设dp[i][j]表示放到第i个原料,放完后的原料数量为j个最大的耐久度。

可以用dp[i-1][k]转移而来,其中k:j-1<=k<=min(w,j+s-1)。

你发现了什么,可以用单调队列优化!

即维护一个长度为s的单调队列。

//把这个题当做单调队列优化dp的板子貌似不是很友好…………

但是这个题细节是真的多……先来欣赏一下曲折的AC过程:

  1. ans和dp一开始要初始化为很小的负数,否则负数点全挂。(remove WA)
  2. 注意数组开的范围:>5500,而不是5000。(remove RE)
  3. 注意时刻判断队列是否为空。(remove RE)(35分到手)
  4. ans、dp、q需要累加,所以会超过int范围。(remove WA)(50分到手)
  5. 因为dp更新时要加上a[i]*j,若a[i]为int,则int*5500会炸int,所以a也要开long long。(remove WA)(55分到手)
  6. ans要初始化为long long的很小的负数值,很小的int不够。(remove WA)(100分到手)

我***……

AC代码

 

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<queue>
 6 using namespace std;
 7 int n,m,s;
 8 long long ans=-0x3f3f3f3f3f3f,dp[5505][5505],qq[5505],a[5505];
 9 int main(){
10     memset(dp,-0x3f,sizeof(dp)); 
11     cin>>n>>m>>s;
12     for(int i=1;i<=n;i++) cin>>a[i];
13     for(int i=1;i<=n;i++){
14         deque<int> q;
15         for(int j=min(m,i-1);j>0;j--){
16             if(q.empty()) q.push_back(j);
17             else{
18                 while(!q.empty()&&q.front()>j+s) q.pop_front();
19                 while(!q.empty()&&dp[i-1][q.back()]<=dp[i-1][j]) q.pop_back();
20                 q.push_back(j);
21             }
22             qq[j]=dp[i-1][q.front()];
23         }
24         while(!q.empty()&&q.front()>s) q.pop_front();
25         if(!q.empty()) qq[0]=dp[i-1][q.front()];
26         else qq[0]=0; 
27         for(int j=1;j<=min(m,i);j++){
28             dp[i][j]=qq[j-1]+a[i]*j;
29         }
30     }
31     for(int i=1;i<=m;i++) ans=max(ans,dp[n][i]);
32     cout<<ans;
33     return 0;
34 }

 

posted @ 2020-10-18 00:05  尹昱钦  阅读(199)  评论(0编辑  收藏  举报