寒假:Day11

Day11

提高课继续,补题从未停止!!

关于背包问题的一些问题

首先我们必须要清楚它问的是啥:

  1. 求前 i i i 个物品中体积不超过 V V V 的最大价值
  2. 求前 i i i 个物品中体积恰好是 V V V 的最大价值(最小代价)
  3. 求前 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[i1,j],f[i1,jv]+w) 我们必须要保证 j ≥ v j ≥ v jv ,因为如果 v > j v > j v>j 的话那就不可能是恰好体积为 j j j 了,最后我们看第三个问题,他的初始化和第二个问题是一样的,在此不多赘述,但是状态转移方程不一样,因为是至少是体积为 j j j 的情况,也就是说体积大于 j j j 也都是合法的,就不需要保证 j ≥ v j ≥ v jv 了,假设,当前第 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[i1,j],f[i1,max(0,jv)+w]) 因为 j − v j - v jv 是负数,那我们把他变成0就好,最后总结下就是:

  1. 求前 i i i 个物品中体积不超过 V V V 的最大价值 [初始化全部为0,保证 v ≤ j v ≤ j vj]
  2. 求前 i i i 个物品中体积恰好是 V V V 的最大价值(最小代价) [初始化 f [ 0 ] = 0 f[0] = 0 f[0]=0,其他为无穷,保证 v ≤ j v≤j vj]
  3. 求前 i i i 个物品中体积至少是 V V V 的最小代价 [初始化 f [ 0 ] = 0 f[0] = 0 f[0]=0,其他为无穷,无需保证 v ≤ j v≤j vj]

最后放一道第三种情况例题: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;
}

分组背包求具体方案问题

1013. 机器分配 - AcWing题库

先利用分组背包思想求出最大值,然后再反推出方案即可

#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。。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值