文章目录
正文开始之前的基础和进阶版本
情况一:从前 i 个物品中选,且总体积不超过 j 的最大价值,初始化是 f[i , k] = 0,0 <= i <= n, 0 <= k <= m(只会求价值的最大值)
01背包问题
例子:给你一堆物品,每个物品有一定的体积和对应的价值,每个物品只能选一个,求总体积不超过 m 的最大价值
输入 :n 个物品, 总体积不超过 m, 接下来是 n 个物品的体积和价值
4 5
1 2
2 4
3 4
4 5
输出:最大价值
8
二维代码
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
int n, m;
int f[N][N];
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i ++ ) {
int v, w;
cin >> v >> w;
for (int j = 0; j <= m; j ++ ) {
f[i][j] = f[i - 1][j]; //不选
if (j >= v) f[i][j] = max(f[i][j], f[i - 1][j - v] + w);
}
}
cout << f[n][m];
return 0;
}
状态压缩
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
int n, m;
int f[N];
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i ++ ) {
int v, w;
cin >> v >> w;
for (int j = m; j >= v; j -- ) {
f[j] = max(f[j], f[j - v] + w);
}
}
cout << f[m];
return 0;
}
情况二:从前 i 个物品中选,且总体积恰好是 j
01背包问题
(1)求价值最大值:初始化 f[0][0] = 0, 其余是 -INF
例子:给你一堆物品,每个物品有一定的体积和对应的价值,每个物品只能选一个,求总体积恰好是 j 的最大价值
输入
4 5
1 2
2 4
3 4
4 5
输出
8
二维代码
#include <bits/stdc++.h>
using namespace std;
const int N = 110, INF = 0x3f3f3f3f;
int n, m;
int f[N][N];
int main() {
cin >> n >> m;
/*初始时刻,只能当恰好体积等于 自身v时, j - v 为 0 , 从 f[0][0] 转移过来
第二个同理 可从f[1][0]转移, 价值即为自身价值
第一个物品: 0 2 -1044266557 -1044266557 -1044266557 -1044266557
前两个物品: 0 2 4 6 -1044266553 -1044266553
前三个物品: 0 2 4 6 6 8
前四个物品: 0 2 4 6 6 8
初始化为无穷是为了让所有物品最开始都只能从自身转移
对比体积不超过背包容量,初始化全零的情况,可以看出,每个物品在后面至少都可是自身带来的价值
第一个物品: 0 2 2 2 2 2
前两个物品: 0 2 4 6 6 6
前三个物品: 0 2 4 6 6 8
前四个物品: 0 2 4 6 6 8
*/
memset(f, -0x3f, sizeof f);
f[0][0] = 0;
for (int i = 1; i <= n; i ++ ) {
int v, w;
cin >> v >> w;
for (int j = 0; j <= m; j ++ ) {
f[i][j] = f[i - 1][j]; //先不选从上一状态转移过来,即最开始一个物品的话,就是从第 0 行转移,不能敲好等于自身体积,则会被置为 -INF
if (j >= v) f[i][j] = max(f[i][j], f[i - 1][j - v] + w); //再判断当前是否可以加入当前这个物品
}
}
int res = 0;
for (int i = 0; i <= m; i ++ ) res = max(res, f[n][i]);
cout << res;
return 0;
}
状态压缩
#include <bits/stdc++.h>
using namespace std;
const int N = 110, INF = 0x3f3f3f3f;
int n, m;
int f[N];
int main() {
cin >> n >> m;
/*
状态压缩后,每一层的转移状态,需要注意这里需要从体积的最大值 m, 递减到 v, 是为了借助上一次的状态, 只取到 v 是因为之前的由于 < v,所以不选,即可不操作
第一个物品: 0 2 -1044266557 -1044266557 -1044266557 -1044266557
前两个物品: 0 2 4 6 -1044266553 -1044266553
前三个物品: 0 2 4 6 6 8
前四个物品: 0 2 4 6 6 8
*/
memset(f, -0x3f, sizeof f);
f[0] = 0;
for (int i = 1; i <= n; i ++ ) {
int v, w;
cin >> v >> w;
for (int j = m; j >= v; j -- ) {
f[j] = max(f[j], f[j - v] + w);
}
}
int res = 0;
for (int i = 1; i <= m; i ++ ) res = max(res, f[i]);
cout << res;
return 0;
}
(2)求价值最小值:初始化 f[0][0] = 0, 其余是 INF
例子:给你一堆物品,每个物品有一定的体积和对应的价值,每个物品只能选一个,求总体积恰好是j的最小价值
输入
4 5
1 2
2 4
3 4
4 5
输出
7
二维代码
#include <bits/stdc++.h>
using namespace std;
const int N = 110, INF = 0x3f3f3f3f;
int n, m;
int f[N][N];
int main() {
cin >> n >> m;
/*
首先要控制状态转移一定只能从恰好能等于该体积的状态转移过来,所以除了f[0][0]为0外,其余都为 INF
接下来就是取最大或者最小即可
第一个物品: 0 2 1061109567 1061109567 1061109567 1061109567
前两个物品: 0 2 4 6 1061109567 1061109567
前三个物品: 0 2 4 4 6 8
前四个物品: 0 2 4 4 5 7
*/
memset(f, 0x3f, sizeof f);
f[0][0] = 0;
for (int i = 1; i <= n; i ++ ) {
int v, w;
cin >> v >> w;
for (int j = 0; j <= m; j ++ ) {
f[i][j] = f[i - 1][j];
if (j >= v) f[i][j] = min(f[i][j], f[i - 1][j - v] + w);
}
}
int res = 0;
for (int i = 0; i <= m; i ++ ) res = max(res, f[n][i]);
cout << res;
return 0;
}
状态压缩
#include <bits/stdc++.h>
using namespace std;
const int N = 110, INF = 0x3f3f3f3f;
int n, m;
int f[N];
int main() {
cin >> n >> m;
/*
状态压缩同01背包求最大值的状态压缩
*/
memset(f, 0x3f, sizeof f);
f[0] = 0;
for (int i = 1; i <= n; i ++ ) {
int v, w;
cin >> v >> w;
for (int j = m; j >= v; j -- ) {
f[j] = min(f[j], f[j - v] + w);
}
}
int res = 0;
for (int i = 0; i <= m; i ++ ) res = max(res, f[i]);
cout << res;
return 0;
}
完全背包问题
(1)求价值最大值:初始化 f[0][0] = 0, 其余是 -INF
例子:给你一堆物品,每个物品有一定的体积和对应的价值,每个物品可以选无数多个,求总体积恰好是 j 的最大价值
输入
4 5
1 2
2 4
3 4
4 5
输出
10
二维代码
#include <iostream>
#include <cstring>
using namespace std;
const int N = 110, INF = 0x3f3f3f3f;
int n, m;
int f[N][N];
int main()
{
cin >> n >> m;
memset(f, -0x3f, sizeof f);
f[0][0] = 0;
/*
有了 01 背包问题求恰好体积为 j 的经验,其实完全背包同理,
初始化值,让每个背包只能当恰好等于自己体积时,才发生转移,
依次对背包选0 - 无穷个,这里其实已经进行了状态的压缩,
完全背包问题本身是三重循环,第三重是选当前背包的个数
第一种背包( 1 2 ): 0 2 4 6 8 10
第二个背包( 2 4 ): 0 2 4 6 8 10
第三个背包( 3 4 ): 0 2 4 6 8 10
第四个背包( 4 5 ): 0 2 4 6 8 10
*/
for (int i = 1; i <= n; i ++ )
{
int v, w;
cin >> v >> w;
for (int j = 0; j <= m; j ++ )
{
f[i][j] = f[i - 1][j];
if (j >= v) f[i][j] = max(f[i][j], f[i][j - v] + w);
}
}
int res = 0;
for (int i = 0; i <= m; i ++ ) res = max(res, f[n][i]);
cout << res;
return 0;
}
☆完全背包问题的经典状态压缩过程
f[i , j ] = max( f[i-1,j] , f[i-1,j-v]+w , f[i-1,j-2*v]+2*w , f[i-1,j-3*v]+3*w , .....)
f[i , j-v]= max( f[i-1,j-v] , f[i-1,j-2*v] + w , f[i-1,j-3*v]+2*w , .....)
由上两式,可得出如下递推关系:
f[i][j]=max(f[i,j-v] + w , f[i-1][j]) //即选与不选两种状态
状态压缩
#include <iostream>
#include <cstring>
using namespace std;
const int N = 110, INF = 0x3f3f3f3f;
int n, m;
int f[N];
int main()
{
cin >> n >> m;
/*
这里在进行状态压缩时, 可以看到,与01背包的区别在于, j 是 从 v ~ m
是因为由完全背包问题的状态压缩推导可以看到,需要用到的是当前状态的值,
而01背包用到的是前一状态的值
*/
memset(f, -0x3f, sizeof f);
f[0] = 0;
for (int i = 1; i <= n; i ++ )
{
int v, w;
cin >> v >> w;
for (int j = v; j <= m; j ++ )
{
f[j] = max(f[j], f[j - v] + w);
}
}
int res = 0;
for (int i = 0; i <= m; i ++ ) res = max(res, f[i]);
cout << res;
return 0;
}
(2)求价值最小值:初始化 f[0][0] = 0, 其余是 INF
例子:给你一堆物品,每个物品有一定的体积和对应的价值,每个物品可以选无数多个,求总体积恰好是 j 的最小价值
输入
4 5
1 2
2 4
3 4
4 5
输出
7
二维代码
#include <bits/stdc++.h>
using namespace std;
const int N = 110, INF = 0x3f3f3f3f;
int n, m;
int f[N][N];
int main()
{
cin >> n >> m;
memset(f, 0x3f, sizeof f);
f[0][0] = 0;
for (int i = 1; i <= n; i ++ )
{
int v, w;
cin >> v >> w;
for (int j = 0; j <= m; j ++ )
{
f[i][j] = f[i - 1][j];
if (j >= v) f[i][j] = min(f[i][j], f[i][j - v] + w);
}
}
int res = 0;
for (int i = 0; i <= m; i ++ ) res = max(res, f[n][i]);
cout << res;
return 0;
}
状态压缩
#include <iostream>
#include <cstring>
using namespace std;
const int N = 110, INF = 0x3f3f3f3f;
int n, m;
int f[N];
int main()
{
cin >> n >> m;
memset(f, 0x3f, sizeof f);
f[0] = 0;
for (int i = 1; i <= n; i ++ )
{
int v, w;
cin >> v >> w;
for (int j = v; j <= m; j ++ )
{
f[j] = min(f[j], f[j - v] + w);
}
}
int res = 0;
for (int i = 0; i <= m; i ++ ) res = max(res, f[i]);
cout << res;
return 0;
}
情况三:从前 i 个物品中选,且总体积至少是 j ,初始化是 f[0][0] = 0, 其余是 INF(只会求价值的最小值)
例子:给你一堆物品,每个物品有一定的体积和对应的价值,每个物品只能选一次,求总体积至少是j的最小价值
输入
4 5
1 2
2 4
3 4
4 5
输出
7
题解
1.初始化时:和恰好等于体积 j 的初始化方式相同,因为至少包含了恰好等于,而计算超出部分时,则需要通过 f[i][0] 转移过来
2.max(0, j - v) ,即当体积此时不能装下时, 表示超出了,也满足,所以直接可以从f[i][0] 表示过来,再 + w, 表示可以装,只是体积超出了。
3.注意我们要求的是价值的最小值,所以体积不小于时,我们尽可能利用恰好就等于自身体积来算,所以利用最小的值即,f[i][0]进行转移
第一种背包( 1 2 ): 0 2 4 6 8 10
第二个背包( 2 4 ): 0 2 4 6 8 10
第三个背包( 3 4 ): 0 2 4 4 6 8
第四个背包( 4 5 ): 0 2 4 4 5 7
二维代码
#include <iostream>
#include <cstring>
using namespace std;
const int N = 110, INF = 0x3f3f3f3f;
int n, m;
int f[N][N];
int main()
{
cin >> n >> m;
/*
max(0, j - v) ,即当体积此时不能装下时, 表示超出了,也满足,所以直接可以从f[i][0] 表示过来,再 + w, 表示可以装,只是体积超出了
第一种背包( 1 2 ): 0 2 4 6 8 10
第二个背包( 2 4 ): 0 2 4 6 8 10
第三个背包( 3 4 ): 0 2 4 4 6 8
第四个背包( 4 5 ): 0 2 4 4 5 7
*/
memset(f, INF, sizeof f);
f[0][0] = 0;
for (int i = 1; i <= n; i ++ )
{
int v, w;
cin >> v >> w;
for (int j = 0; j <= m; j ++ )
{
f[i][j] = min(f[i - 1][j], f[i][max(0, j - v)] + w);//即使物品体积比j大,j - v < 0,也能选,等价于f[i - 1][0]
}
}
cout << f[n][m] << endl;
return 0;
}
状态压缩
#include <iostream>
#include <cstring>
using namespace std;
const int N = 110, INF = 0x3f3f3f3f;
int n, m;
int f[N];
int main()
{
cin >> n >> m;
/*
题目求至少是 j
0 2 1061109567 1061109567 1061109567 1061109567
0 2 4 6 1061109567 1061109567
0 2 4 4 6 8
0 2 4 4 5 7
*/
memset(f, 0x3f, sizeof f);
f[0] = 0;
for (int i = 1; i <= n; i ++ )
{
int v, w;
cin >> v >> w;
for (int j = m; j >= 0; j -- )
{
f[j] = min(f[j], f[max(0, j - v)] + w);//即使物品体积比j大,j - v < 0,也能选,等价于f[0]
}
}
cout << f[m] << endl;
return 0;
}
如有错误之处,欢迎指正~~~
转载标题:【动态规划】背包求最大值最小值问题 转载地址:https://www.123yun.com/article/1290.html
智能动环监控系统 智慧工地监控监控系统 直流系统绝缘监测装置作用 直达资金监控系统