012整数转罗马数字

题意

罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。

字符          数值
I             1
V             5
X             10
L             50
C             100
D             500
M             1000
复制代码

例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。

通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:

  • I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
  • X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
  • C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。

给你一个整数,将其转为罗马数字。

n的范围为1 <= n <= 3999

难度

中等

示例

示例 1:

输入: num = 3
输出: "III"
复制代码

示例 2:

输入: num = 4
输出: "IV"
复制代码

示例 3:

输入: num = 9
输出: "IX"
复制代码

示例4

输入: num = 58
输出: "LVIII"
解释: L = 50, V = 5, III = 3.
复制代码

示例5

输入: num = 1994
输出: "MCMXCIV"
解释: M = 1000, CM = 900, XC = 90, IV = 4.
复制代码

分析

从题目的描述中,我们可以得出这样一个结论,罗马数字非常贪心,比如说 4 是 IV,而不是 IIII,9 是 IX,而不是 VIIII,40 是 XL,而不是 XXXX,90 是 XC,而不是 LXXXX,每次都挑最优的算法来表示数字,对吧?

比如说今年是 2024 年,那用罗马数字来表示的话,就先挑最大的 M,也就是 1000,2000 就是 MM,20 就是 XX,24 就是 XXIV,合在一起就是 MMXXIV。

换句话说,每次都用最优解。

这就要引出今天的主角,贪心算法。一种在每一步选择中都采取当前状态下最优的选择(即最“贪心”的选择),以期望最终结果是全局最优的算法。这就是贪心算法的思想。

以找零钱问题为例,假设你是一名售货员,需要给顾客找零钱。你的目标是用最少的货币数量找给顾客相应的零钱。

假设你的货币有 1 元、5 元、10 元、20 元、50 元、100 元这 6 种,需要找给顾客 628 元,你会怎么找呢?

可以先找 100 元,六张;再找 20 元,一张;接着找 5 元,一张;最后找 1 元,三张。

你不可能从 1 元开始找。

整数转罗马数字其实就非常适合贪心算法。题目也告诉我们这样一个对应关系了:

int[] values = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
String[] symbols = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};
复制代码

我们用一个 for 循环遍历 values,再用一个 while 循环判断当前数字是否大于等于 values[i],如果是的话,就减去 values[i],并将 values 对应的罗马数字 symbols 添加到结果字符串中。

来看题解:

class Solution {
    public String intToRoman(int num) {
        // 定义两个数组,一个表示特定的罗马数字,一个表示对应的整数值
        int[] values = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
        String[] symbols = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};

        // 使用 StringBuilder 存储最终的罗马数字字符串
        StringBuilder sb = new StringBuilder();

        // 遍历整数值数组
        for (int i = 0; i < values.length && num > 0; i++) {
            // 当前数字还大于等于 values[i] 时,继续循环
            // 这意味着我们可以从 num 中减去 values[i],并添加对应的罗马数字到结果中
            while (num >= values[i]) {
                num -= values[i]; // 从 num 中减去 values[i]
                sb.append(symbols[i]); // 添加对应的罗马数字到结果字符串
            }
        }

        // 将 StringBuilder 转换为字符串并返回
        return sb.toString();
    }
}
复制代码
  • 定义两个数组:values 和 symbols。values 数组包含了各个罗马数字对应的整数值,而 symbols 数组包含了相应的罗马数字字符串。
  • 接着,使用一个 StringBuilder 对象 sb 来构建最终的罗马数字字符串。
  • 在主循环中,遍历 values 数组。对于每个元素 values[i],我们检查 num 是否大于或等于 values[i]。
  • 如果条件满足,我们就从 num 中减去 values[i],并将相应的罗马数字 symbols[i] 添加到 sb 中。这个过程一直持续到 num 小于当前的 values[i]。
  • 这个循环确保了我们总是优先使用最大的罗马数字,直到无法再使用更大的数字为止。
  • 最后,将 StringBuilder 对象转换为字符串并返回。

题解效率还不错,击败了 98% 的用户

总结

本题的关键是通过整数转罗马数字的表象,找出贪心算法的本质。

这种找本质的能力,其实是需要大量的练习才能掌握的。

比如说,还是找零的问题,面值不再是 1 元、2 元、5 元、10 元、20 元、50 元、100 元,而是 1 元、3 元、4 元,要你找零 6 元。

如果是贪心算法,结果会是 1 张 1 元,2 张 1 元。

但更优的选择应该是 2 张 3 元,对吧?

贪心算法的局限性就在于它总是寻求局部最优解,而不一定能得到全局最优解。在每一步,它都做出在当前情况下看起来是最好的决策,而没有考虑这些决策如何影响未来的结果。对于那些局部最优解不等同于全局最优解的问题,贪心算法可能无法得到正确的结果。

其次,这道题涉及到 Java 基础知识就不多了:

  • 数组
  • for循环
  • while循环
  • StringBuilder

扣链接:. - 力扣(LeetCode)

  • 19
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值