【题目来源】AcWing 3. 完全背包问题
解题思路
完全背包和
0
/
1
0/1
0/1背包的唯一区别在于,每一个物品变为了每一种物品且都有无数件,可以重复装入。
const int N = 1050;
struct res{
int v, w;
} q[N];
int n, V, dp[N][N];
int main(){
cin >> n >> V;
for(int i = 1; i <=n ; i ++) cin >> q[i].v >> q[i].w;
return 0;
}
要注意,此时的状态转移方程会发生改变
动态规划思想:
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j],代表当背包空间为
j
j
j 时,我面前有前
i
i
i 种物品,能够装入的最大价值,推导如下:
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]分为多种情况,在
d
p
[
i
−
1
]
[
j
]
dp[i - 1][j]
dp[i−1][j]的基础上:
①不装入或者说装入
0
0
0个第
i
i
i 种物品,
d
p
[
i
−
1
]
[
j
]
dp[i - 1][j]
dp[i−1][j];
②装入
1
1
1个第
i
i
i 种物品,
d
p
[
i
−
1
]
[
j
−
q
[
i
]
.
v
]
+
q
[
i
]
.
w
dp[i - 1][j - q[i].v] + q[i].w
dp[i−1][j−q[i].v]+q[i].w;
……
③装入
k
k
k个第
i
i
i 种物品,
d
p
[
i
−
1
]
[
j
−
k
∗
q
[
i
]
.
v
]
+
k
∗
q
[
i
]
.
w
dp[i - 1][j - k * q[i].v] + k * q[i].w
dp[i−1][j−k∗q[i].v]+k∗q[i].w。
……
推式1:dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - v] + w, dp[i - 1][j - 2 * v] + 2 * w, … , dp[i - 1][j - k * v] + k * w, … )
推式2:dp[i][j - v] = max(dp[i - 1][j - v], dp[i - 1][j - 2 * v] + w, dp[i - 1][j - 3 * v] + 2 * w, … , dp[i - 1][j - (k + 1)v] + k * w, … )
推式2变形:dp[i][j - v] + w = max(dp[i - 1][j - v] + w, dp[i - 1][j - 2 * v] + 2 * w, dp[i - 1][j - 3 * v] + 3 * w, … , dp[i - 1][j - (k + 1) * v] + (k + 1) * w, … )
结论:dp[i][j] = max(dp[i - 1][j], dp[i][j - v] + w);
dp[i][j] = max(dp[i - 1][j], dp[i][j - q[i].v] + q[i].w);
亦可解释为,当背包空间为 j j j 时,有前 i i i 种物品可装入,但是我不知道现在想要装入的这前 i i i 种物品已经装入了几个,所以考虑空间为 j − q [ i ] . v j - q[i].v j−q[i].v 时,前 i i i 种物品的装入情况,即 d p [ i ] [ j − q [ i ] . v ] dp[i][j - q[i].v] dp[i][j−q[i].v] 。
for(int i = 1; i <= n; i ++)
for(int j = 0; j <= V; j ++){
dp[i][j] = dp[i - 1][j];
if(j >= q[i].v) dp[i][j] = max(dp[i - 1][j], dp[i][j - q[i].v] + q[i].w);
}
优化为一位数组, d p [ i ] [ j ] dp[i][j] dp[i][j]的值取决于 d p [ i − 1 ] [ j ] dp[i - 1][j] dp[i−1][j]或 d p [ i ] [ j − q [ i ] . v ] dp[i][j - q[i].v] dp[i][j−q[i].v],即上一行同一列或同一行前列,数组前面的值会发生改变并且决定后面的值,所以应该从前往后遍历。
for(int i = 1; i <= n; i ++)
for(int j = q[i].v; j <= V; j ++) //从前往后遍历
dp[j] = max(dp[j], dp[j - q[i].v] + q[i].w);
cout << dp[V];