动态规划问题之背包模型(18题)
Posted _luckylight
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态规划问题之背包模型(18题)相关的知识,希望对你有一定的参考价值。
背包问题是动态规划问题的一大类型,下面我们对这个进行总结。
以 Acwing y中总结的 几个类型,我写了几个题解
应用知识点
- 01背包、完全背包 空间压缩的写法
- 多维费用的背包问题,以及状态的不同表示对复杂度的 影响
- 完全背包问题的三种求解方法 O ( N M S ) , O ( N M l o g S ) , O ( N M ) O(NMS), O(NMlogS),O(NM) O(NMS),O(NMlogS),O(NM)
- dp维度关系等于,小于等于,大于等于对初始化的影响(潜水员)
- dp关键信息最小值,最大值,方案数对初始化的影响
一、采药
Acwing 题目链接
非常裸的 01 背包
#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int f[N][N];
int n, m;
int main()
{
cin >> m >> n;
memset(f, 0, sizeof f);
for (int i = 1; i <= n; i ++ ) {
static int t, v;
scanf("%d%d", &t, &v);
for (int j = 0; j <= m; j ++ ) {
f[i][j] = f[i - 1][j];
if (j >= t) {
f[i][j] = max(f[i][j], f[i - 1][j - t] + v);
}
}
}
cout << f[n][m] << endl;
return 0;
}
二、装箱问题
ACwing 题目链接
对于该问题,我给出两个解法(其实都类似 )
第一种
f[i][j] 表示利用前 i 个箱子,体积恰好为 j 的方案是否可行,是一个 bool 数组
那么
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
]
∣
f
[
i
−
1
]
[
j
−
v
[
i
]
]
f[i][j] = f[i-1][j] | f[i-1][j - v[i]]
f[i][j]=f[i−1][j]∣f[i−1][j−v[i]]
初始化时,
f
[
0
]
[
0
]
f[0][0]
f[0][0] 置为 true,其余置为 false
下面是进行空间优化的写法
#include <bits/stdc++.h>
using namespace std;
const int N = 40, M = 40010;
int f[M];
int n, m;
int cost[N];
// f[i][j] 表示前 i个物品,体积 恰好等于 j 是否可以
// 然后进行状态压缩
int main()
{
// input
cin >> m >> n;
for (int i = 1; i <= n; i ++ )
scanf("%d", &cost[i]);
f[0] = true;
for (int i = 1; i <= n; i ++ ) {
for (int j = m; j >= cost[i]; j -- ) {
f[j] |= f[j - cost[i]];
}
}
for (int i = m; ; i -- ) {
if (f[i]) {
cout << m - i << endl;
break;
}
}
return 0;
}
第二种写法
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示的是对前 i 个箱子,体积小于等于 j 时候的最大体积
f
[
i
]
[
j
]
=
m
a
x
(
f
[
i
−
1
]
[
j
]
,
f
[
i
−
1
]
[
j
−
v
[
i
]
]
+
v
[
i
]
f[i][j] =max(f[i-1][j], f[i-1][j-v[i]] + v[i]
f[i][j]=max(f[i−1][j],f[i−1][j−v[i]]+v[i]
这样的话,初始化全部为 0
而且最后输出结果也比较简单
m
−
f
[
n
]
[
m
]
m - f[n][m]
m−f[n][m]
下面给出空间优化之后的代码
#include <bits/stdc++.h>
using namespace std;
const int N = 40010;
int f[N];
int n, m;
// f[i][j] 表示前 i 个物品,体积小于等于 j 时候可以容纳的最大体积
// 然后进行状态压缩
int main()
{
cin>>m>>n;
int v;
memset(f, 0, sizeof f);
for(int i = 1; i <= n; i++){
cin>>v;
for(int j = m; j >= v; j--){
f[j] = max(f[j], f[j-v]+v);
}
}
cout<<m-f[m]<<endl;
return 0;
}
三、宠物小精灵值收服
Acwing 题目链接
本题是一个二维费用的 01 背包,理解起来不拿,不过题目属实有点长,而且 皮卡丘 的血量为 0 也会抓取失败,这是一个比较坑的点
下面,我给出两个dp的解决方案
方案一
f
[
i
]
[
j
]
[
k
]
f[i][j][k]
f[i][j][k] 表示对前 i 个怪兽进行遍历,消耗精灵球数量小于等于j,消耗血量小于等于 k,的最大抓捕量
f
[
i
]
[
j
]
[
k
]
=
m
a
x
(
f
[
i
−
1
]
[
k
]
[
k
]
,
f
[
i
−
1
]
[
j
−
c
o
s
t
1
i
]
[
k
−
c
o
s
t
2
i
]
)
f[i][j][k] = max(f[i-1][k][k], f[i-1][j-cost1_i][k-cost2_i])
f[i][j][k]=max(f[i−1][k][k],f[i−1][j−cost1i][k−cost2i])
对应的代码如下
#include <bits/stdc++.h>
using namespace std;
const int N = 1010, M = 510, K = 110, INF = 0x3f3f3f3f;
int cost1[K], cost2[K], n, m, k;
int f[N][M];
int main()
{
// input
cin >> n >> m >> k;
m --;
for (int i = 1; i <= k; i ++ ) {
scanf("%d%d", &cost1[i], &cost2[i]);
}
for (int i = 1; i <= k; i ++ ) {
for (int j = n; j >= cost1[i]; j -- ) {
for (int k = m; k >= cost2[i]; k -- ) {
f[j][k] = max(f[j][k], f[j - cost1[i]][k - cost2[i]] + 1);
}
}
}
int ans1, ans2;
ans1 = f[n][m];
for (int j = m; ; j -- ) {
if (ans1 == 0) {
ans2 = 0;
break;
}
if (f[n][j] == ans1) {
ans2 = j;
} else {
break;
}
}
printf("%d %d\\n", ans1, m - ans2 + 1);
return 0;
}
但是我们将复杂度考虑进去的话,
O
(
K
N
M
)
O(KNM)
O(KNM)有时候会过大,万一被卡怎么办? 给出方案二
方案二
f
[
i
]
[
M
]
[
K
]
f[i][M][K]
f[i][M][K] 遍历前面i个精灵,统计的是在 体力恰好为 m, 捕捉恰好为 k 时候的最小消耗求的数量
复杂度
O
(
K
K
M
)
O(KKM)
O(KKM)快了那么一点点
f
[
i
]
[
[
j
]
[
k
]
=
m
i
n
(
f
[
i
−
1
]
[
j
]
[
k
]
,
f
[
i
−
1
]
[
m
−
c
o
s
t
2
i
]
[
k
−
1
]
+
c
o
s
t
1
i
)
f[i][[j][k]=min(f[i-1][j][k], f[i-1][m-cost2_i][k-1]+cost1_i)
f[i][[j][k]=min(f[i−1][j][k],f[i−1][m−cost2i][k−1]+cost1i)
代码如下
#include <bits/stdc++.h>
using namespace std;
const int N = 1010, M = 510, K = 110, INF = 0x3f3f3f3f;
int n, m, k1;
int cost1[K], cost2[N];
int f[M][K]; // 统计的是在 体力恰好为 m, 捕捉恰好为 k 时候的最小消耗求的数量
int main()
{
// input
cin >> n >> m >> k1;
for (int i = 1; i <= k1; i ++ ) {
scanf("%d%d", &cost1[i], &cost2[i]);
}
memset(f, 0x3f, sizeof f); f[0][0] = 0;
for (int i = 1; i <= k1; i ++ ) {
for (int j = m - 1; j >= cost2[i]; j -- ) {
for (int k = k1; k >= 1; k -- ) {
f[j][k] = min(f[j][k], f[j - cost2[i]][k - 1] + cost1[i]);
}
}
}
int ans1 = 0, ans2 = 0;
for (int j = 0; j <= m - 1; j ++ ) {
for (int k = 0; k < k1; k ++) {
if (f[j][k] <= n) {
if (k > ans1) {
ans1 = k, ans2 = j;
} else if (f[j][k] == n && j < ans2) {
ans2 = j;
}
}
}
}
printf("%d %d\\n", ans1, m - ans2);
以上是关于动态规划问题之背包模型(18题)的主要内容,如果未能解决你的问题,请参考以下文章