[题解]P5858 Golden Sword

P5858 「SWTR-3」Golden Sword

第一道自己想出递推公式并且成功\(AC\)\(dp\)绿题。

题意简述

\(n\)种原料,每个原料有一个耐久度\(a[i]\),必须按照\(1,2,…,n\)的顺序放入炼金锅。但是炼金锅的容量是有限的,只能放\(w\)个原料,所以在每次放入原料之前,都可以选择取出\(0\sim s\)个原料再放当前的原料。

放入第\(i\)个原料后,所锻造的武器的总耐久度就会增加\(len*a[i]\)\(len\)表示当前炼金锅中原料个数)。

询问最大的“武器总耐久度”。

数据范围:

输入输出格式

输入:\(n,w,s\),而后\(n\)个整数\(a_1,a_2,…,a_n\)

输出:一行,表示最终答案。

思路简述

用下面的样例进行演示:

7 4 2
-5 3 -1 -4 7 -6 5

输出:17

如图,用\(f\)作为\(dp\)数组。\(f[i][j]\)表示放了前\(i\)个,放完后锅中还有\(j\)个的最大值。

递推公式:\(f[i][j]=a[i]*j+max(f[i-1][k])(j-1\leq k\leq min(w,j+s-1))\)

例如下图求\(f[6][2]\),就是用\(max(绿色部分)+2倍的紫色部分\)

怎么推导呢?我们发现\(dp[6][2]\)肯定可以由\(dp[5][1]\)推来的,因为\(f[5][1]\)的基础上,不拿走任何元素就直接放入第\(6\)个元素,状态就是\(f[6][2]\)

那么\(dp[6][2]\)还能由哪些状态推来呢?别忘了每放一个原料前,我们可以拿出最多\(s=2\)个元素。所以\(f[6][2]\)自然也能通过\(f[5][2]\)(拿\(1\)个元素)、\(f[5][3]\)(拿\(2\)个元素)。

这样我们可以写出代码(见Code部分#1)。

优化

然而这份代码提交上去,面对数据更大的点会\(TLE\)。这是因为我们每求一次最大值都要遍历上一行。怎么解决这个问题呢?没错,这就是「单调队列优化\(dp\)

单调队列我先开个坑,到时候填。

使用\(maxx[j]\)表示当前行即\(f[i]\)中,以\(j\)结尾,往前长度为\(min(j,s+1)\)的最大值。每次求完这一行的\(f\),就处理出\(maxx\)数组供下一行使用。

这样求\(f[i][j]\)时,就用\(maxx[j+s-1]\)就好了。

此时我们发现\(f\)也可以优化空间为一维(与HDU1024 Max Sum Plus Plus有异曲同工之妙,无论是在推导过程还是优化方面),这样既优化了时间,还优化了空间,一举两得。

注意递推完,下一行最多用到\(max(i+s,w+s)\),所以维护\(maxx\)时只需要循环至\(max(i+s,w+s)\)即可。

时间复杂度\(O(nw)\),代码见#2。

Code

#1
#include<bits/stdc++.h>
#define int long long
using namespace std;
int s,w,n,a[5010];
int f[5010][5010];
signed main(){
	cin>>n>>w>>s;
	for(int i=1;i<=n;i++) cin>>a[i];
	memset(f,0x80,sizeof f);//long long极小值
	for(int i=0;i<=w;i++) f[0][i]=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=w;j++){
			if(j>i) break;
			for(int k=j-1;k<j+s;k++){
				if(k>w) break;
				f[i][j]=max(f[i][j],f[i-1][k]);
			}
			f[i][j]+=a[i]*j;
			cout<<f[i][j]<<" ";
		}
		cout<<endl;
	}
	int ans=LLONG_MIN;
	for(int i=1;i<=w;i++) ans=max(ans,f[n][i]);
	cout<<ans;
	return 0;
}
#2
#include<bits/stdc++.h>
#define int long long
using namespace std;
int s,w,n,a[5010],maxx[5010];
int f[5010];
deque<int> q;
signed main(){
	cin>>n>>w>>s;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++){
		for(int j=1;j<=w;j++){
			if(j>i) break;
			f[j]=maxx[j+s-1]+a[i]*j;
		}
		q.clear();
		for(int j=1;j<=w+s;j++){
			if(j>i+s) break;
			if(j<=i&&j<=w){//如果i个元素加完了,就只出不入
				while(!q.empty()&&f[j]>=f[q.front()]) q.pop_back();
				q.push_back(j);
			}
			if(q.front()+s+1<=j) q.pop_front();
			maxx[j]=f[q.front()];
		}
	}
	int ans=LLONG_MIN;
	for(int i=1;i<=w;i++) ans=max(ans,f[i]);
	cout<<ans;
	return 0;
}
posted @ 2024-03-26 22:50  Sinktank  阅读(18)  评论(0编辑  收藏  举报
★CLICK FOR MORE INFO★ TOP-BOTTOM-THEME
Enable/Disable Transition
Copyright © 2023 ~ 2024 Sinktank - 1328312655@qq.com
Illustration from 稲葉曇『リレイアウター/Relayouter/中继输出者』,by ぬくぬくにぎりめし.