Linear DP example (Luogu P5858 Golden Sword) and its monotonic queue optimization

topic description

Manufacturing a golden sword requires n kinds of raw materials, numbered 1 to n, and the solid value ai of the raw material numbered i.
Alchemy is very particular about the order of putting in the raw materials, so Little E must put these raw materials into the alchemy pot in the order of 1 to n.
However, the capacity of the alchemy pot is very limited, it can only hold w raw materials at most.
Fortunately, before each raw material is put in, Little E can take some raw materials out of it, and the number cannot exceed s.
We define the durability of the i-th raw material as: the total number of raw materials in the pot (including the raw materials being put in) × ai when the i-th raw material is put in, then the durability of the sword is the sum of the durability of all raw materials.
Of course, Little E wants to make his sword as durable as possible, so that he can carry it into more battles and ask for the maximum durability.

Note: Here "the total number of raw materials in the pot when the i-th raw material is put in includes the raw materials that are being put into the pot, please refer to the sample for details.

input format

In the first line, three integers n, w, s.
In the second line, n integers a1, a2,...,an.

output format

One integer per line, indicating the maximum durability.

sample

input#1

5 3 3
1 3 2 4 5

Output #1

40

answer

According to the meaning of the question, the raw materials put into the pot can only be placed in order and must be put in.
Therefore, a dp[i][j] can be set to represent the maximum durability that can be obtained when the i-th raw material is placed, and the total number of raw materials in the pot is j.
It can be seen that the state transition equation can be written as:
dp[i][j] = max{dp[i-1][k]} + a[i] * j (j-1 <= k <= j+s-1 )
The detailed code is as follows:

#include<iostream>
#include<cstdio>
#include<cstring>
#define INF 0x3f3f3f3f
using namespace std;
const int N = 5000 + 10;
typedef long long ll;
 
ll n, w, s;
ll a[N];
ll dp[N][N];
int main() {
    
    
	scanf("%lld%lld%lld", &n, &w, &s);
	memset(dp, -INF, sizeof(dp));
	for (int i = 1; i <= n; i++) {
    
    
		scanf("%lld", &a[i]);
	}
	dp[0][0] = 0;
	for (int i = 1; i <= n; i++) {
    
    
		for (int j = 1; j <= w; j++) {
    
    
			for (int k = j - 1; k <= min(j + s - 1, w); k++)
				dp[i][j] = max(dp[i][j], dp[i - 1][k] + a[i] * j);
		}
	}
	ll ans = -INF;
	for (int i = 1; i <= w; i++)
		ans = max(dp[n][i], ans);
	cout << ans;
	return 0;
}

The time complexity of this code can reach O(nw^2).
You can consider using a monotonic queue to optimize max
because j-1 <= k <= j+s-1, so you need to change the cycle of j from large to small.
Use the queue simulated by the array q[] to save the maximum value of dp that has been obtained earlier.
Maintain the head of the team , out of range q[head] > j + s - 1, kick out the head of the team, head++.
Maintain the tail of the team , dp[i-1][j-1] is the latest to be kicked out of the head of the team, if the dp[i - 1][q[tail]] at the current tail of the team is higher than dp[i - 1 ][j - 1] is even smaller, definitely not as good as dp[i - 1][j - 1]. Just tail--.

The detailed code is as follows


#include<iostream>
#include<cstdio>
#include<cstring>
#define INF 0x3f3f3f3f
using namespace std;
const int N = 5000 + 10;
typedef long long ll;

ll n, w, s;
ll a[N];
ll dp[N][N], q[N];
int main() {
    
    
	scanf("%lld%lld%lld", &n, &w, &s);
	memset(dp, -INF, sizeof(dp));
	for (int i = 1; i <= n; i++) {
    
    
		scanf("%lld", &a[i]);
	}
	dp[0][0] = 0;
	for (int i = 1; i <= n; i++) {
    
    
		int l = 1, r = 1;
		q[l] = w;
		for (int j = w; j >= 1; j--) {
    
    
			//维护队尾
			while (q[l] > j + s - 1 && l <= r) l++;
			//维护队头
			while (dp[i - 1][q[r]] < dp[i - 1][j - 1] && l <= r) r--;
			//将最近的一个(最晚可能被踢出队的)入队尾
			q[++r] = j - 1;
			//队头一定是可行范围内的最大的(最优)
			dp[i][j] = dp[i - 1][q[l]] +  a[i] * j;
		}
	}
	ll ans = -INF;
	for (int i = 1; i <= w; i++)
		ans = max(dp[n][i], ans);
	cout << ans;
	return 0;
}

Guess you like

Origin blog.csdn.net/Salvator_/article/details/123634874