LeetCode 1449. Form Largest Integer With Digits That Add up to Target【动态规划/字符串】困难

本文介绍了一种高效算法,用于在给定整数数组cost和目标总成本target的条件下,找到能以最大成本绘制且符合规则的整数。通过完全背包与优化策略,解决了成本分配和整数构造的问题,最终提供了一个字符串形式的答案。

Given an array of integers cost and an integer target. Return the maximum integer you can paint under the following rules:

  • The cost of painting a digit (i+1) is given by cost[i] (0 indexed).
  • The total cost used must be equal to target.
  • Integer does not have digits 0.

Since the answer may be too large, return it as string.

If there is no way to paint any integer given the condition, return "0".

Example 1:

Input: cost = [4,3,2,5,6,7,2,5,5], target = 9
Output: "7772"
Explanation:  The cost to paint the digit '7' is 2, and the digit '2' is 3. Then cost("7772") = 2*3+ 3*1 = 9. You could also paint "977", but "7772" is the largest number.
Digit    cost
  1  ->   4
  2  ->   3
  3  ->   2
  4  ->   5
  5  ->   6
  6  ->   7
  7  ->   2
  8  ->   5
  9  ->   5

Example 2:

Input: cost = [7,6,5,5,5,6,8,7,8], target = 12
Output: "85"
Explanation: The cost to paint the digit '8' is 7, and the digit '5' is 5. Then cost("85") = 7 + 5 = 12.

Example 3:

Input: cost = [2,4,6,2,4,6,4,4,4], target = 5
Output: "0"
Explanation: It's not possible to paint any integer with total cost equal to target.

Example 4:

Input: cost = [6,10,15,40,40,40,40,40,40], target = 47
Output: "32211"

Constraints:

  • cost.length == 9
  • 1 <= cost[i] <= 5000
  • 1 <= target <= 5000

题意:给你一个整数数组 cost 和一个整数 target 。请你返回满足如下规则可以得到的 最大 整数:

  • 给当前结果添加一个数位(i + 1)的成本为 cost[i] (cost 数组下标从 0 开始)。
  • 总成本必须恰好等于 target 。
  • 添加的数位中没有数字 0 。

由于答案可能会很大,请你以字符串形式返回。

如果按照上述要求无法得到任何整数,请你返回 "0" 。


解法 完全背包

完全背包+恰好问题+输出答案,一开始的写法如下:

class Solution {
public:
    string largestNumber(vector<int>& cost, int target) {
        vector<bool> dp(target + 1, false); 
        vector<string> ans(target + 1, "");
        dp[0] = true, ans[0] = "";
        //dp[0]=true, dp[i]表示是否能够满足成本恰好为i,此时得到的最大整数为ans[i] (完全背包+恰好问题)
        //从[物品]9个数位(1-9)选择,背包容量为成本大小,数位i+1的代价为cost[i],价值为对应的数位i+1
        for (int i = 0; i < 9; ++i) {
            for (int j = cost[i]; j <= target; ++j) {
                dp[j] = dp[j] || dp[j - cost[i]];
                if (dp[j - cost[i]]) {
                    string a = ans[j], b = ans[j - cost[i]], c = ""; //在b上添加数位i+1使其最大 
                    for (int k = 0; k < b.size(); ++k) {
                        if (('1' + i) > b[k]) {
                            c = b.substr(0, k) + to_string(i + 1) + b.substr(k);
                            break;
                        }
                    }
                    if (c.empty()) c = b + to_string(i + 1);
                    //比较字符串代表的整数的大小
                    if (a.size() < c.size()) ans[j] = c;
                    else if (a.size() == c.size()) ans[j] = max(a, c);  
                }
            } 
        }
        return dp[target] ? ans[target] : "0";
    }
};

运行效率很低:

执行用时:684 ms, 在所有 C++ 提交中击败了5.28% 的用户
内存消耗:460.1 MB, 在所有 C++ 提交中击败了13.03% 的用户

后来发现,遍历的顺序由小到大,当前的数位一定是最大的,可以直接放到最高位。它一定大于等于整数的低位,从而保证了整数最大化,因此可以减少中间的 for 循环:

class Solution {
public:
    string largestNumber(vector<int>& cost, int target) {
        vector<bool> dp(target + 1, false); 
        vector<string> ans(target + 1, "");
        dp[0] = true, ans[0] = ""; 
        for (int i = 0; i < 9; ++i) {
            for (int j = cost[i]; j <= target; ++j) {
                dp[j] = dp[j] || dp[j - cost[i]];
                if (dp[j - cost[i]]) {
                    string a = ans[j], &&b = to_string(i + 1) + ans[j - cost[i]]; //在b上添加数位i+1使其最大  
                    if (a.size() < b.size()) ans[j] = b;
                    else if (a.size() == b.size()) ans[j] = max(a, b);  
                }
            } 
        }
        return dp[target] ? ans[target] : "0";
    }
};

运行效率大大提升:

执行用时:292 ms, 在所有 C++ 提交中击败了39.44% 的用户
内存消耗:343.8 MB, 在所有 C++ 提交中击败了34.86% 的用户

原先的 dp[i] 表示其中数位的 cost 相加是否等于 i ,而 ans[i] 用来输出对应的答案。新的优化来自于,我们可以用 "#" 表示不合法的情况,即无法得到的目标值,避免不合法的转移,同时初始化 dp[0] = "" ,这是合法的情况:

class Solution {
public:
    string largestNumber(vector<int>& cost, int target) {
        //dp[0]=true, dp[i]表示是否能够满足成本恰好为i,此时得到的最大整数 (完全背包+恰好问题)
        //从[物品]9个数位(1-9)选择,背包容量为成本大小,数位i+1的代价为cost[i],价值为对应的数位i+1
        vector<string> dp(target + 1, "#");  
        dp[0] = "";
        for (int i = 0; i < 9; ++i) {
            for (int j = cost[i]; j <= target; ++j) {
                if (dp[j - cost[i]] != "#") {
                    string a = dp[j], &&b = to_string(i + 1) + dp[j - cost[i]]; //在b上添加数位i+1使其最大 
                    //比较字符串代表的整数的大小
                    if (a.size() < b.size()) dp[j] = b;
                    else if (a.size() == b.size()) dp[j] = max(a, b);  
                }
            } 
        }
        return dp[target] != "#" ? dp[target] : "0";
    }
};

运行效率如下:

执行用时:256 ms, 在所有 C++ 提交中击败了46.83% 的用户
内存消耗:343.9 MB, 在所有 C++ 提交中击败了34.86% 的用户
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

memcpy0

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值