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;
}
posted @ 2021-08-24 19:36  TheBestQAQ  阅读(92)  评论(0)    收藏  举报