Luogu P1776 宝物筛选(单调队列优化dp)
题面:

题解:
经典的多重背包问题,但是会\(tle\)
设\(dp[i][j]\) 表示前 \(i\)个物品,容量为 \(j\) 的最大价值,那么转移很好转移:
\(dp[i][j]=max(dp[i-1][j-k \times w[i]]+k \times v[i])\)
考虑如何优化:
设 $j=k_1\times w[i]+d $
那么原式子变为 \(dp[i][j]=max(dp[i-1][(k_1-k) \times w[i]+d]-(k_1-k) \times v[i])+k_1 \times v[i]\)
设\(k_1-k=t\) ,那么 \(dp[i][j]=max(dp[i-1][t \times w_i+d]-t \times v_i)+k_1 \times v_i\)
而\(j=k_1 \times w_i +d\) ,那么 \(dp[i][k_1 \times w_i +d]=max(dp[i-1][t \times w_i+d]-t \times v_i)+k_1 \times v_i\)
可以发现,这时两个式子的第二维的形式是一样的,那么我们可以枚举 \(d\) 和 \(k_1\) ,每次将 \(dp[i-1][k_1 \times w_i+d]- k_1 \times v_i\) 放入单调队列中,这样单调队列里面存的就是 \(dp[i-1][t \times w_i+d]-t \times v_i\) 。
这样枚举的复杂度就是 \(O(nW)\)
代码:
#pragma GCC diagnostic error "-std=c++11"
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
#include<ctime>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef pair<int,int> pii;
const int mod=1e9+7;
const int MAXN=2e5+5;
const int inf=0x3f3f3f3f;
ll dp[2][MAXN];
ll q[MAXN][2],m[MAXN],w[MAXN],v[MAXN];
int main()
{
int n, cost;
cin >> n >> cost;
for (int i = 1; i <= n;i++){
cin >> v[i] >> w[i] >> m[i];
}
int now = 0;
for (int i = 1; i <= n;i++)
{
for (int d = 0; d < w[i];d++)
{
int l = 0, r = -1;
for (int k = 0; k * w[i] + d <= cost;k++)
{
while(l<=r&&q[l][0]<k-m[i])
l++;
while(l<=r&&q[r][1]<dp[now^1][k*w[i]+d]-k*v[i])
r--;
r++;
q[r][0] = k;
q[r][1] = dp[now ^ 1][k * w[i] + d]-k*v[i];
dp[now][k * w[i] + d] = q[l][1]+k*v[i];
}
}
now ^= 1;
}
now ^= 1;
cout << dp[now][cost] << endl;
}

浙公网安备 33010602011771号