多重背包 单调队列优化

参考 洛谷P1776 宝物筛选_NOI导刊2010提高(02)(多重背包,单调队列)

d p [ i ] [ j ] dp[i][j] dp[i][j] 为前 i i i 个物品,重量不超过 j j j 的最大价值, w w w 为重量, v v v 为价值, m m m 为数量, W W W 为背包大小
d p [ i ] [ j ] = m a x { d p [ i − 1 ] [ j − k w i ] + k v i } 0 < = k < = m i n ( m i , j w i ) dp[i][j] = max\{dp[i-1][j-kw_i] + kv_i\} \qquad 0<=k<=min(m_i,\frac{j}{w_i}) dp[i][j]=max{ dp[i1][jkwi]+kvi}0<=k<=min(mi,wij)
去掉第一维后,简化为:
d p [ j ] = m a x { d p [ j − k w ] + k v } dp[j] = max\{dp[j-kw] + kv\} dp[j]=max{ dp[jkw]+kv}
时间复杂度: O ( n m W ) O(nmW) O(nmW)


仔细观察对于一个 j j j 而言,只有 j w i \frac{j}{w_i} wij 个有效转移,那么就可以去掉很多无用的状态
j = k 1 w + d j = k_1w+d j=k1w+d ,那么枚举 d d d ,再枚举 k 1 k_1 k1 ,再枚举 k k k,复杂度是一样的
d p [ j ] = m a x { d p [ k 1 w + d − k w ] + k v } dp[j] = max\{dp[k_1w+d-kw] + kv\} dp[j]=max{ dp[k1w+dkw]+kv}
= m a x { d p [ ( k 1 − k ) w + d ] − ( k 1 − k ) v } + k 1 v = max\{dp[(k_1-k)w+d] - (k_1-k)v\} + k_1v =max{ dp[(k1k)w+d](k1k)v}+k1v
注意 k 1 k_1 k1 k k k 是无关的,所以可以分离出来
因此对于一个 d d d 而言,有意义的状态只有 W − d w \frac{W-d}{w} wWd 种,算上 d d d ,总的状态只有 O ( W ) O(W) O(W)


g k = d p [ k w + d ] − k v g_k = dp[kw+d] - kv gk=dp[kw+d]kv
0 < = k < = m i n ( m , j w ) 0<=k<=min(m,\frac{j}{w}) 0<=k<=min(m,wj) j w = k 1 w + d w = k 1 + d w > k 1 \frac{j}{w} = \frac{k_1w+d}{w} =k_1+\frac{d}{w} >k_1 wj=wk1w+d=k1+wdk1
m a x { 0 , k 1 − m } < = k 1 − k < = k 1 ) max\{0,k_1-m\}<=k_1-k<=k_1) max{ 0,k1m}<=k1k<=k1)
也就是对于每个 k 1 k_1 k1,只能从 m a x { g k ∣ k ∈ [ m a x { 0 , k 1 − m } , k 1 ] } max\{g_k|k∈[max\{0,k_1−m\},k_1]\} max{ gkk[max{ 0,k1m},k1]} 中转移
就不用枚举 k k k了,用单调队列优化维护下标在 [ k 1 − m , k 1 ] [k_1−m,k_1] [k1m,k1] 范围内的决策值即可
时间复杂度就是惊为天人的 O ( n W ) O(nW) O(nW)

模板题:P1776 宝物筛选

#include
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
const int maxn = 1e2 + 5;
const int maxw = 4e4 + 5;
int n, W, dp[maxn][maxw];
int v[maxn], w[maxn], m[maxn];
int q[maxw][2];

int main() {
	scanf("%d%d", &n, &W);
	for(int i=1; i<=n; i++) {
		scanf("%d%d%d", v+i, w+i, m+i);
		for(int d=0; d

你可能感兴趣的:(ACM,-,背包DP,多重背包,单调队列优化)