数位成本和为目标值的最大数字
编号:0014
试题来源:leetcode
题目描述
给定整数数组cost
和一个整数target
。返回满足如下规则的可以得到的最大整数:
- 给当前结果添加一个数位
i+1
的成本为cost[i]
(cost
数组的下标从0开始) - 总成本必须恰好等于
target
- 添加的位数中没有数字0
返回类型string
如果无法得到任何整数,那么返回0
。
解答算法
算法思路
很显然这是一个0-1完全背包问题。
我用的是暴力做法,时间复杂度太高,失败了
target
是目标的花费,显然,cost
数组中的每一个值都是正数,因为一旦存在0
,那么可以无穷添加,就没有最大值了。所以
c
o
s
t
[
i
]
≥
1
cost[i]\geq 1
cost[i]≥1。因此可以设置一个temp数组,其中元素是string
类型,temp[i]
存储当总花费为i
的时候产生的最大string
。
显然有如下递推公式
temp[target] = max{temp[target - cost[i]]+cost[i],cost[i] + temp[target - cost[i]]}
,如果
t
a
r
g
e
t
−
c
o
s
t
[
i
]
=
=
0
target - cost[i] == 0
target−cost[i]==0说明这是第一个添加的元素。如果
t
a
r
g
e
t
−
c
o
s
t
[
i
]
<
0
target - cost[i] < 0
target−cost[i]<0说明,该target
没有满足条件的string
;如果temp[target - cost[i]]
对应的string
为空,说明它不存在,那么自然不能在一个不存在的字符串上加数位因此也不存在。
通过递推公式,一个个遍历temp[i]
,最后就能够解决问题
代码实现
class Solution {
public:
string largestNumber(vector<int>& cost, int target) {
int len = cost.size();
unordered_map<int, int> cost2num; //key存放的是花费,对应得是下标
for(int i = 0; i < len; i++) { //更新花费map
if(cost2num.find(cost[i]) == cost2num.end() || cost2num[cost[i]] < i + 1) {
cost2num[cost[i]] = i + 1;
}
}
vector<string> temp(target + 1, ""); //vector数组得长度为target+1,初始值均为空字符串
for(int i = 1; i <= target; i++) { //从1开始,逐渐提升target的值
for(const auto& [cost, num]: cost2num) {
int index = i - cost; //如果添加了当前数位,是在temp[i-cost]的基础上添加的
if(index == 0 || index > 0 && temp[index] != "") { //如果index == 0说明当前添加的i是第一个数位;如果index>0,那么需要temp[i-cost]有合适的取值,才能添加新的数位
string second = to_string(num);
string new_str = temp[index] > second? temp[index] + second: second + temp[index]; //产生添加数位之后的string
if(new_str.size() > temp[i].size() || new_str.size() == temp[i].size() && new_str > temp[i]) { //和当前temp[i]进行对比,谁大选谁
temp[i] = new_str;
}
}
}
}
return temp[target] == ""? "0": temp[target]; //非空返回string,否则返回0
}
};
复杂度分析
- 时间复杂度:整个过程中遍历了 O ( t a r g e t ) O(target) O(target)的时间,同时每一次都进行了比较操作以及对9个可以添加的数位的比较,最终的时间复杂度为 O ( n ) O(n) O(n)
- 空间复杂度:申请了 O ( t a r g e t ) O(target) O(target)的额外空间,时间复杂度为 O ( n ) O(n) O(n)