Day11
提高课继续,补题从未停止!!
关于背包问题的一些问题
首先我们必须要清楚它问的是啥:
- 求前 i i i 个物品中体积不超过 V V V 的最大价值
- 求前 i i i 个物品中体积恰好是 V V V 的最大价值(最小代价)
- 求前 i i i 个物品中体积至少是 V V V 的最小代价
这里我们以01背包问题,虽然这三种的状态转移方程都大径相同,但是针对不同的问题却有着不同的初始化
我们一般是考虑一个不选的情况下有什么不一样的地方,第一个问题 f [ 0 , j ] f[0, j] f[0,j] 表示一个都不选且体积不超过 j j j 的情况,这显然是合法的,所以我们把它初始化为0就可以了,那么如果是第二个问题 f [ 0 , j ] f[0, j] f[0,j] 表示的是一个都不选且体积恰好是 j j j 的情况,这个就和第一个问题不一样的,一个都不选体积只能是0,所以只有在 f [ 0 , 0 ] f[0, 0] f[0,0] 的时候是合法的,其他的 f [ 0 , j ] f[0, j] f[0,j] 都是不合法的,我们就根据题目意思把它初始化成无穷大或者是无穷小即可,注意这里还有一个问题,就是关于状态转移方程当中 f [ i , j ] = m a x ( f [ i − 1 , j ] , f [ i − 1 , j − v ] + w ) f[i, j] = max(f[i - 1, j], f[i - 1, j - v] + w) f[i,j]=max(f[i−1,j],f[i−1,j−v]+w) 我们必须要保证 j ≥ v j ≥ v j≥v ,因为如果 v > j v > j v>j 的话那就不可能是恰好体积为 j j j 了,最后我们看第三个问题,他的初始化和第二个问题是一样的,在此不多赘述,但是状态转移方程不一样,因为是至少是体积为 j j j 的情况,也就是说体积大于 j j j 也都是合法的,就不需要保证 j ≥ v j ≥ v j≥v 了,假设,当前第 i i i 个物品他的体积是大于 j j j 的,那我是不是前面都不选,只选它也是满足题意的,所以他的状态转移方程也变一下: f [ i , j ] = m i n ( f [ i − 1 , j ] , f [ i − 1 , m a x ( 0 , j − v ) + w ] ) f[i, j] = min(f[i - 1, j], f[i - 1, max(0, j - v) + w]) f[i,j]=min(f[i−1,j],f[i−1,max(0,j−v)+w]) 因为 j − v j - v j−v 是负数,那我们把他变成0就好,最后总结下就是:
- 求前 i i i 个物品中体积不超过 V V V 的最大价值 [初始化全部为0,保证 v ≤ j v ≤ j v≤j]
- 求前 i i i 个物品中体积恰好是 V V V 的最大价值(最小代价) [初始化 f [ 0 ] = 0 f[0] = 0 f[0]=0,其他为无穷,保证 v ≤ j v≤j v≤j]
- 求前 i i i 个物品中体积至少是 V V V 的最小代价 [初始化 f [ 0 ] = 0 f[0] = 0 f[0]=0,其他为无穷,无需保证 v ≤ j v≤j v≤j]
最后放一道第三种情况例题:1020. 潜水员 - AcWing题库
#include<bits/stdc++.h>
using namespace std;
const int N = 100;
int f[N][N];
int main(void)
{
int n, m;
cin >> n >> m;
int t;
cin >> t;
memset(f, 0x3f, sizeof f); // 初始化
f[0][0] = 0;
while (t--) {
int a, b, c;
cin >> a >> b >> c;
for (int i = n; i >= 0; i--) {
for (int j = m; j >= 0; j--) {
f[i][j] = min(f[i][j], f[max(0, i - a)][max(0, j - b)] + c); // 状态转移
}
}
}
cout << f[n][m] << endl ;
return 0;
}
分组背包求具体方案问题
先利用分组背包思想求出最大值,然后再反推出方案即可
#include<bits/stdc++.h>
using namespace std;
const int N = 110;
int f[N][N], w[N][N];
int n, m;
int way[N]; // 记录方案
int main(void)
{
cin >> n >> m ;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
cin >> w[i][j];
for (int i = 1; i <= n; i++) // 枚举组数
for (int j = 0; j <= m; j++) // 枚举体积
for (int k = 0; k <= j; k++) // 枚举组内
f[i][j]= max(f[i][j], f[i - 1][j - k] + w[i][k]);
cout << f[n][m] << endl ;
int j = m; // 先把体积置为最大体积
for (int i = n; i >= 1; i--) { // 从后往前枚举
for (int k = 0; k <= j; k++) { // 枚举体积
if (f[i][j] == f[i - 1][j - k] + w[i][k]) { // 如果存在转换关系,则代表选择了这个
way[i] = k;
j -= k;
break;
}
}
}
for (int i = 1; i <= n; i++) cout << i << " " << way[i] << endl ; // 从前往后输出
return 0;
}
又水了一天,太菜了呜呜呜qwq。。。。