# 1449. 数位成本和为目标值的最大数字
# 题目地址(1449. 数位成本和为目标值的最大数字)
<https://leetcode-cn.com/problems/form-largest-integer-with-digits-that-add-up-to-target/>
## 题目描述
```
<pre class="calibre18">```
给你一个整数数组 cost 和一个整数 target 。请你返回满足如下规则可以得到的 最大 整数:
给当前结果添加一个数位(i + 1)的成本为 cost[i] (cost 数组下标从 0 开始)。
总成本必须恰好等于 target 。
添加的数位中没有数字 0 。
由于答案可能会很大,请你以字符串形式返回。
如果按照上述要求无法得到任何整数,请你返回 "0" 。
示例 1:
输入:cost = [4,3,2,5,6,7,2,5,5], target = 9
输出:"7772"
解释:添加数位 '7' 的成本为 2 ,添加数位 '2' 的成本为 3 。所以 "7772" 的代价为 2*3+ 3*1 = 9 。 "997" 也是满足要求的数字,但 "7772" 是较大的数字。
数字 成本
1 -> 4
2 -> 3
3 -> 2
4 -> 5
5 -> 6
6 -> 7
7 -> 2
8 -> 5
9 -> 5
示例 2:
输入:cost = [7,6,5,5,5,6,8,7,8], target = 12
输出:"85"
解释:添加数位 '8' 的成本是 7 ,添加数位 '5' 的成本是 5 。"85" 的成本为 7 + 5 = 12 。
示例 3:
输入:cost = [2,4,6,2,4,6,4,4,4], target = 5
输出:"0"
解释:总成本是 target 的条件下,无法生成任何整数。
示例 4:
输入:cost = [6,10,15,40,40,40,40,40,40], target = 47
输出:"32211"
提示:
cost.length == 9
1 <= cost[i] <= 5000
1 <= target <= 5000
```
```
## 前置知识
- 数组
- 动态规划
- 背包问题
## 公司
- 暂无
## 思路
由于数组可以重复选择,因此这是一个完全背包问题。
### 01 背包
对于 01 背包问题,我们的套路是:
```
<pre class="calibre18">```
<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-params">0</span> to N:
<span class="hljs-keyword">for</span> j <span class="hljs-keyword">in</span> <span class="hljs-params">1</span> to V + <span class="hljs-params">1</span>:
dp[j] = max(dp[j], dp[j - cost[i])
```
```
而一般我们为了处理边界问题,我们一般会这么写代码:
```
<pre class="calibre18">```
<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-params">1</span> to N + <span class="hljs-params">1</span>:
<span class="hljs-title"># 这里是倒序的,原因在于这里是01背包。</span>
<span class="hljs-keyword">for</span> j <span class="hljs-keyword">in</span> V to <span class="hljs-params">0</span>:
dp[j] = max(dp[j], dp[j - cost[i - <span class="hljs-params">1</span>])
```
```
其中 dp\[j\] 表示只能选择前 i 个物品,背包容量为 j 的情况下,能够获得的最大价值。
> dp\[j\] 不是没 i 么? 其实我这里 i 指的是 dp\[j\]当前所处的循环中的 i 值
### 完全背包问题
回到问题,我们这是完全背包问题:
```
<pre class="calibre18">```
<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-params">1</span> to N + <span class="hljs-params">1</span>:
<span class="hljs-title"># 这里不是倒序,原因是我们这里是完全背包问题</span>
<span class="hljs-keyword">for</span> j <span class="hljs-keyword">in</span> <span class="hljs-params">1</span> to V + <span class="hljs-params">1</span>:
dp[j] = max(dp[j], dp[j - cost[i - <span class="hljs-params">1</span>])
```
```
### 为什么 01 背包需要倒序,而完全背包则不可以
实际上,这是一个骚操作,我来详细给你讲一下。
其实要回答这个问题,我要先将 01 背包和完全背包退化二维的情况。
对于 01 背包:
```
<pre class="calibre18">```
<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-params">1</span> to N + <span class="hljs-params">1</span>:
<span class="hljs-keyword">for</span> j <span class="hljs-keyword">in</span> V to <span class="hljs-params">0</span>:
dp[i][j] = max(dp[i - <span class="hljs-params">1</span>][j], dp[i - <span class="hljs-params">1</span>][j - cost[i - <span class="hljs-params">1</span>])
```
```
注意等号左边是 i,右边是 i - 1,这很好理解,因为 i 只能取一次嘛。
那么如果我们不降序遍历会怎么样呢?
![](https://img.kancloud.cn/81/f8/81f8664f87d2eeed79f4e2658a480784_1114x594.jpg)
如图橙色部分表示已经遍历的部分,而让我们去用\[j - cost\[i - 1\]\] 往前面回溯的时候,实际上回溯的是 dp\[i\]j - cost\[i - 1\]\],而不是 dp\[i - 1\]j - cost\[i - 1\]\]。
如果是降序就可以了,如图:
![](https://img.kancloud.cn/82/a6/82a62cef214772a2076ae5f0c1240f09_1088x552.jpg)
这个明白的话,我们继续思考为什么完全背包就要不降序了呢?
我们还是像上面一样写出二维的代码:
```
<pre class="calibre18">```
<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-params">1</span> to N + <span class="hljs-params">1</span>:
<span class="hljs-keyword">for</span> j <span class="hljs-keyword">in</span> <span class="hljs-params">1</span> to V + <span class="hljs-params">1</span>:
dp[i][j] = max(dp[i - <span class="hljs-params">1</span>][j], dp[i][j - cost[i - <span class="hljs-params">1</span>])
```
```
由于 i 可以取无数次,那么正序遍历正好可以满足,如上图。
### 恰好装满 VS 可以不装满
题目有两种可能,一种是要求背包恰好装满,一种是可以不装满(只要不超过容量就行)。而本题是要求`恰好装满`的。而这两种情况仅仅影响我们`dp数组初始化`。
- 恰好装满。只需要初始化 dp\[0\] 为 0, 其他初始化为负数即可。
- 可以不装满。 只需要全部初始化为 0,即可,
原因很简单,我多次强调过 dp 数组本质上是记录了一个个自问题。 dp\[0\]是一个子问题,dp\[1\]是一个子问题。。。
有了上面的知识就不难理解了。 初始化的时候,我们还没有进行任何选择,那么也就是说 dp\[0\] = 0,因为我们可以通过什么都不选达到最大值 0。而 dp\[1\],dp\[2\]...则在当前什么都不选的情况下无法达成,也就是无解,因为为了区分,我们可以用负数来表示,当然你可以用任何可以区分的东西表示,比如 None。
### 回到本题
而这道题和普通的完全背包不一样,这个是选择一个组成的最大数。由小学数学知识`一个数字的全排列中,按照数字降序排列是最大的`,我这里用了一个骚操作,那就是 cost 从后往前遍历,因为后面表示的数字大。
## 代码
```
<pre class="calibre18">```
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Solution</span>:</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">largestNumber</span><span class="hljs-params">(self, cost: List[int], target: int)</span> -> str:</span>
dp = [<span class="hljs-params">0</span>] + [float(<span class="hljs-string">'-inf'</span>)] * target
<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(<span class="hljs-params">9</span>, <span class="hljs-params">0</span>, <span class="hljs-params">-1</span>):
<span class="hljs-keyword">for</span> j <span class="hljs-keyword">in</span> range(<span class="hljs-params">1</span>, target+<span class="hljs-params">1</span>):
<span class="hljs-keyword">if</span> j >= cost[i - <span class="hljs-params">1</span>]:
dp[j] = max(dp[j], (dp[j-cost[i - <span class="hljs-params">1</span>]] * <span class="hljs-params">10</span>) + i)
<span class="hljs-keyword">return</span> str(dp[target]) <span class="hljs-keyword">if</span> dp[target] > <span class="hljs-params">0</span> <span class="hljs-keyword">else</span> <span class="hljs-string">'0'</span>
```
```
**复杂度分析**
- 时间复杂度:O(target))O(target))O(target))
- 空间复杂度:O(target)O(target)O(target)
## 扩展
最后贴几个我写过的背包问题,让大家看看历史是多么的相似。
![](https://img.kancloud.cn/c2/f5/c2f52308a45a8b8e961bbdef8359c757_1586x830.jpg)([322. 硬币找零(完全背包问题)](https://github.com/azl397985856/leetcode/blob/master/problems/322.coin-change.md))
> 这里内外循环和本题正好是反的,我只是为了"秀技"(好玩),实际上在这里对答案并不影响。
![](https://img.kancloud.cn/9b/eb/9beb32f6a1edd77bf5776b2cecc0404d_1586x508.jpg)([518. 零钱兑换 II](https://github.com/azl397985856/leetcode/blob/master/problems/518.coin-change-2.md))
> 这里内外循环和本题正好是反的,但是这里必须这么做,否则结果是不对的,具体可以点进去链接看我那个题解
所以这两层循环的位置起的实际作用是什么? 代表的含义有什么不同?
本质上:
```
<pre class="calibre18">```
<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-params">1</span> to N + <span class="hljs-params">1</span>:
<span class="hljs-keyword">for</span> j <span class="hljs-keyword">in</span> V to <span class="hljs-params">0</span>:
...
```
```
这种情况选择物品 1 和物品 3(随便举的例子),是一种方式。选择物品 3 个物品 1(注意是有顺序的)是同一种方式。 **原因在于你是固定物品,去扫描容量**。
而:
```
<pre class="calibre18">```
<span class="hljs-keyword">for</span> j <span class="hljs-keyword">in</span> V to <span class="hljs-params">0</span>:
<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-params">1</span> to N + <span class="hljs-params">1</span>:
...
```
```
这种情况选择物品 1 和物品 3(随便举的例子),是一种方式。选择物品 3 个物品 1(注意是有顺序的)也是一种方式。**原因在于你是固定容量,去扫描物品**。
**因此总的来说,如果你认为\[1,3\]和\[3,1\]是一种,那么就用方法 1 的遍历,否则用方法 2。**
大家对此有何看法,欢迎给我留言,我有时间都会一一查看回答。更多算法套路可以访问我的 LeetCode 题解仓库:<https://github.com/azl397985856/leetcode> 。 目前已经 37K star 啦。 大家也可以关注我的公众号《力扣加加》带你啃下算法这块硬骨头。 ![](https://img.kancloud.cn/cf/0f/cf0fc0dd21e94b443dd8bca6cc15b34b_900x500.jpg)
![](https://img.kancloud.cn/7b/b1/7bb115b82aa493e2d2a88f71d5201779_1190x680.jpg)
- Introduction
- 第一章 - 算法专题
- 数据结构
- 基础算法
- 二叉树的遍历
- 动态规划
- 哈夫曼编码和游程编码
- 布隆过滤器
- 字符串问题
- 前缀树专题
- 《贪婪策略》专题
- 《深度优先遍历》专题
- 滑动窗口(思路 + 模板)
- 位运算
- 设计题
- 小岛问题
- 最大公约数
- 并查集
- 前缀和
- 平衡二叉树专题
- 第二章 - 91 天学算法
- 第一期讲义-二分法
- 第一期讲义-双指针
- 第二期
- 第三章 - 精选题解
- 《日程安排》专题
- 《构造二叉树》专题
- 字典序列删除
- 百度的算法面试题 * 祖玛游戏
- 西法的刷题秘籍】一次搞定前缀和
- 字节跳动的算法面试题是什么难度?
- 字节跳动的算法面试题是什么难度?(第二弹)
- 《我是你的妈妈呀》 * 第一期
- 一文带你看懂二叉树的序列化
- 穿上衣服我就不认识你了?来聊聊最长上升子序列
- 你的衣服我扒了 * 《最长公共子序列》
- 一文看懂《最大子序列和问题》
- 第四章 - 高频考题(简单)
- 面试题 17.12. BiNode
- 0001. 两数之和
- 0020. 有效的括号
- 0021. 合并两个有序链表
- 0026. 删除排序数组中的重复项
- 0053. 最大子序和
- 0088. 合并两个有序数组
- 0101. 对称二叉树
- 0104. 二叉树的最大深度
- 0108. 将有序数组转换为二叉搜索树
- 0121. 买卖股票的最佳时机
- 0122. 买卖股票的最佳时机 II
- 0125. 验证回文串
- 0136. 只出现一次的数字
- 0155. 最小栈
- 0167. 两数之和 II * 输入有序数组
- 0169. 多数元素
- 0172. 阶乘后的零
- 0190. 颠倒二进制位
- 0191. 位1的个数
- 0198. 打家劫舍
- 0203. 移除链表元素
- 0206. 反转链表
- 0219. 存在重复元素 II
- 0226. 翻转二叉树
- 0232. 用栈实现队列
- 0263. 丑数
- 0283. 移动零
- 0342. 4的幂
- 0349. 两个数组的交集
- 0371. 两整数之和
- 0437. 路径总和 III
- 0455. 分发饼干
- 0575. 分糖果
- 0874. 模拟行走机器人
- 1260. 二维网格迁移
- 1332. 删除回文子序列
- 第五章 - 高频考题(中等)
- 0002. 两数相加
- 0003. 无重复字符的最长子串
- 0005. 最长回文子串
- 0011. 盛最多水的容器
- 0015. 三数之和
- 0017. 电话号码的字母组合
- 0019. 删除链表的倒数第N个节点
- 0022. 括号生成
- 0024. 两两交换链表中的节点
- 0029. 两数相除
- 0031. 下一个排列
- 0033. 搜索旋转排序数组
- 0039. 组合总和
- 0040. 组合总和 II
- 0046. 全排列
- 0047. 全排列 II
- 0048. 旋转图像
- 0049. 字母异位词分组
- 0050. Pow(x, n)
- 0055. 跳跃游戏
- 0056. 合并区间
- 0060. 第k个排列
- 0062. 不同路径
- 0073. 矩阵置零
- 0075. 颜色分类
- 0078. 子集
- 0079. 单词搜索
- 0080. 删除排序数组中的重复项 II
- 0086. 分隔链表
- 0090. 子集 II
- 0091. 解码方法
- 0092. 反转链表 II
- 0094. 二叉树的中序遍历
- 0095. 不同的二叉搜索树 II
- 0096. 不同的二叉搜索树
- 0098. 验证二叉搜索树
- 0102. 二叉树的层序遍历
- 0103. 二叉树的锯齿形层次遍历
- 105. 从前序与中序遍历序列构造二叉树
- 0113. 路径总和 II
- 0129. 求根到叶子节点数字之和
- 0130. 被围绕的区域
- 0131. 分割回文串
- 0139. 单词拆分
- 0144. 二叉树的前序遍历
- 0150. 逆波兰表达式求值
- 0152. 乘积最大子数组
- 0199. 二叉树的右视图
- 0200. 岛屿数量
- 0201. 数字范围按位与
- 0208. 实现 Trie (前缀树)
- 0209. 长度最小的子数组
- 0211. 添加与搜索单词 * 数据结构设计
- 0215. 数组中的第K个最大元素
- 0221. 最大正方形
- 0229. 求众数 II
- 0230. 二叉搜索树中第K小的元素
- 0236. 二叉树的最近公共祖先
- 0238. 除自身以外数组的乘积
- 0240. 搜索二维矩阵 II
- 0279. 完全平方数
- 0309. 最佳买卖股票时机含冷冻期
- 0322. 零钱兑换
- 0328. 奇偶链表
- 0334. 递增的三元子序列
- 0337. 打家劫舍 III
- 0343. 整数拆分
- 0365. 水壶问题
- 0378. 有序矩阵中第K小的元素
- 0380. 常数时间插入、删除和获取随机元素
- 0416. 分割等和子集
- 0445. 两数相加 II
- 0454. 四数相加 II
- 0494. 目标和
- 0516. 最长回文子序列
- 0518. 零钱兑换 II
- 0547. 朋友圈
- 0560. 和为K的子数组
- 0609. 在系统中查找重复文件
- 0611. 有效三角形的个数
- 0718. 最长重复子数组
- 0754. 到达终点数字
- 0785. 判断二分图
- 0820. 单词的压缩编码
- 0875. 爱吃香蕉的珂珂
- 0877. 石子游戏
- 0886. 可能的二分法
- 0900. RLE 迭代器
- 0912. 排序数组
- 0935. 骑士拨号器
- 1011. 在 D 天内送达包裹的能力
- 1014. 最佳观光组合
- 1015. 可被 K 整除的最小整数
- 1019. 链表中的下一个更大节点
- 1020. 飞地的数量
- 1023. 驼峰式匹配
- 1031. 两个非重叠子数组的最大和
- 1104. 二叉树寻路
- 1131.绝对值表达式的最大值
- 1186. 删除一次得到子数组最大和
- 1218. 最长定差子序列
- 1227. 飞机座位分配概率
- 1261. 在受污染的二叉树中查找元素
- 1262. 可被三整除的最大和
- 1297. 子串的最大出现次数
- 1310. 子数组异或查询
- 1334. 阈值距离内邻居最少的城市
- 1371.每个元音包含偶数次的最长子字符串
- 第六章 - 高频考题(困难)
- 0004. 寻找两个正序数组的中位数
- 0023. 合并K个升序链表
- 0025. K 个一组翻转链表
- 0030. 串联所有单词的子串
- 0032. 最长有效括号
- 0042. 接雨水
- 0052. N皇后 II
- 0084. 柱状图中最大的矩形
- 0085. 最大矩形
- 0124. 二叉树中的最大路径和
- 0128. 最长连续序列
- 0145. 二叉树的后序遍历
- 0212. 单词搜索 II
- 0239. 滑动窗口最大值
- 0295. 数据流的中位数
- 0301. 删除无效的括号
- 0312. 戳气球
- 0335. 路径交叉
- 0460. LFU缓存
- 0472. 连接词
- 0488. 祖玛游戏
- 0493. 翻转对
- 0887. 鸡蛋掉落
- 0895. 最大频率栈
- 1032. 字符流
- 1168. 水资源分配优化
- 1449. 数位成本和为目标值的最大数字
- 后序