[H背包] lc1449. 数位成本和为目标值的最大数字(背包求具体方案+状态定义+边界情况)

该博客介绍了如何利用动态规划解决一个完全背包问题的变种,目标是找到一个由1到9的数字组成的最大字典序数字,使得这些数字的位数成本之和等于给定的目标值。通过将问题转化为01背包问题并进行状态转移,最终得到字典序最大的数字。博客强调了初始化状态的重要性,并提供了具体的解决方案和代码实现。
摘要由CSDN通过智能技术生成

1. 题目来源

链接:1449. 数位成本和为目标值的最大数字

相关:

2. 题目解析

完全背包求具体方案的问题,是 [01背包] 背包问题求具体方案(01背包+求方案数+思维) 的变种。


首先如果要使满足要求的拼的数最大,有两个要求:

  • 位数尽量大
  • 位数相同,字典序最大

有限制的组合问题求最优解,一般会对应到背包问题。 在此就是从 1~9 中选,要求花费总和等于 target,让拼出来的数最大。

抽象为背包问题,背包容量为 target,9 个物品,物品体积为 cost[i],物品价值为 1。

故问题转化为,给定 9 个物品,一个背包,求在恰好装满背包的情况下,总价值最大的所有方案中字典序最大的一个方案。 至此,已经完全转化为 [01背包] 背包问题求具体方案(01背包+求方案数+思维)

但是,背包问题中细节很多,背包恰好装满、至多装满、物品次数限制等。在循环、初始化方面有所不同。

完全背包、01 背包的状态转移方程对比:

f[i][j] 表示前 i 个物品恰好装 j 体积的最大价值
完全背包:f[i][j]=max(f[i-1][j], f[i][j-cost[i]]+1);
01背包:f[i][j]=max(f[i-1][j], f[i-1][j-cost[i]]+1);

这样就能求出最大价值,即最大的位数。求方案就是倒推一遍即可,这个方案就是字典序最大的方案,即要求我们尽可能选 9,如果不能选 9 再选 8,同理往后选即可。

答案为 f[9][target],它有两种转移情况:

  • 不选 9,则 f[9][target]=f[8][target]
  • 选 9 则 f[9][target]=f[9][target-cost[9]]+1
  • 即只需要从后往前进行倒推即可,当选 9 和不选 9 都一样时,我们要将 9 选上,保证字典序尽量大。

注意本题的初始化方式,本题背包定义是前 i 个物品恰好装满 j 体积的最大价值,故当一个物品都没有时,它是没有体积的,无法恰好装满 j 体积,自然没有价值

初始化时,f[0][1~target] 均是非法方案,不能像以往一样直接将其置为 0,而是要将其置为 -INF,视为非法情况。


时间复杂度: O ( n m ) O(nm) O(nm) 物品数量 x 最大容量=9 * 5000

空间复杂度: O ( n m ) O(nm) O(nm)


class Solution {
public:
    string largestNumber(vector<int>& cost, int target) {
        vector<vector<int>> f(10, vector<int>(target + 1));

        // 本题保证恰好装满背包,即 0 物品时,体积一定是 0,体积不为 0 则均是非法情况,赋为最小值即可
        for (int i = 1; i <= target; i ++ ) f[0][i] = -1e8;

        for (int i = 1; i <= 9; i ++ )
            for (int j = 0; j <= target; j ++ ) {
                f[i][j] = f[i - 1][j];
                if (j >= cost[i - 1]) f[i][j] = max(f[i][j], f[i][j - cost[i - 1]] + 1);
            }

        // 无解情况,1 个数都没选
        if (f[9][target] < 1) return "0";
        
        // 倒推求方案,保证字典序最大
        string res;
        for (int i = 9, j = target; i; i -- ) {
            while (j >= cost[i - 1] && f[i][j] == f[i][j - cost[i - 1]] + 1) {
                res += to_string(i);
                j -= cost[i - 1];
            }
        }

        return res;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ypuyu

如果帮助到你,可以请作者喝水~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值