洛谷 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过程:
- ans和dp一开始要初始化为很小的负数,否则负数点全挂。(remove WA)
- 注意数组开的范围:>5500,而不是5000。(remove RE)
- 注意时刻判断队列是否为空。(remove RE)(35分到手)
- ans、dp、q需要累加,所以会超过int范围。(remove WA)(50分到手)
- 因为dp更新时要加上a[i]*j,若a[i]为int,则int*5500会炸int,所以a也要开long long。(remove WA)(55分到手)
- 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 }