最优硬币匹配问题 | 豆包MarsCode AI刷题

47 阅读3分钟

问题描述

题目链接

现在有 M 种不同面值的硬币(硬币个数数目无限),如何用这些硬币组合为总数额为 N 的钱,同时满足使用硬币的个数最少?

测试样例

输入样例

[1,2,5]
18

输出样例

[5,5,5,2,1]

解决思路

我们需要找到一种组合,使得使用给定的硬币面值组合成目标金额 N,并且使用的硬币数量最少。

数据结构选择

我们可以使用一个数组 dp,其中 dp[i] 表示组成金额 i 所需的最少硬币数量。

算法步骤

  1. 初始化

    • 创建一个大小为 amount + 1 的数组 dp,并将所有元素初始化为一个较大的值(例如 amount + 1),表示初始状态下无法组成该金额。
    • 将 dp[0] 设置为 0,因为组成金额 0 不需要任何硬币。
  2. 状态转移

    • 对于每个金额 i(从 1 到 amount),遍历所有硬币面值 coin
    • 如果 coin 小于或等于 i,则更新 dp[i] 为 min(dp[i], dp[i - coin] + 1)
  3. 结果

    • 如果 dp[amount] 仍然是一个较大的值(例如 amount + 1),则说明无法组成目标金额,返回空数组。
    • 否则,从 dp[amount] 开始,逆向追踪硬币组合,构建结果数组。

代码实现

    public static int solution(int n, int m, String str1, String str2) {
        int maxLen = 0;
        
        // 遍历所有可能的字符 'a' 到 'z'
        for (char targetChar = 'a'; targetChar <= 'z'; targetChar++) {
            int left = 0, right = 0;
            int repairs = 0;
            
            while (right < n) {
                // 如果当前字符不是目标字符,且可以修改
                if (str1.charAt(right) != targetChar && str2.charAt(right) == '1') {
                    repairs++;
                }
                
                // 如果当前字符不是目标字符,且不可修改,则重置窗口
                if (str1.charAt(right) != targetChar && str2.charAt(right) == '0') {
                    left = right + 1;
                    repairs = 0;
                }
                
                // 如果修复次数超过 m,移动左指针
                while (repairs > m) {
                    if (str1.charAt(left) != targetChar && str2.charAt(left) == '1') {
                        repairs--;
                    }
                    left++;
                }
                
                // 计算当前窗口的长度
                maxLen = Math.max(maxLen, right - left + 1);
                
                right++;
            }
        }
        
        return maxLen;
    }

动态规划算法的使用建议

动态规划适用于具有重叠子问题和最优子结构的场景,特别是当问题可以分解成多个子问题,且子问题的解可以通过递推得到时。在使用时,首先要明确状态表示、状态转移方程,并选择合适的存储方式(如自底向上或自顶向下的计算方法)。此外,要注意优化空间复杂度,避免不必要的计算和空间浪费。动态规划的核心思想是通过保存子问题的解来避免重复计算,从而提高效率。通常,动态规划有两种常见的实现方式:自顶向下(递归+记忆化)和自底向上(迭代)。在实现时,首先要明确问题的状态表示,即如何将问题转化为子问题的解,然后定义状态转移方程,确保每个子问题的解只计算一次并存储。动态规划常用于求解如最短路径、最大子数组和背包问题等典型问题,适合处理大规模的问题,但也要注意防止状态空间过大导致的时间和空间复杂度问题。