Leetcode第12题,整数转罗马数字,难度中等

整个题目比较好理解,难度也不大,就算不过脑子,用一串if也基本上可以解决问题,比如

    /** 执行用时:6ms,在所有 Java 提交中击败了52.61%的用户 内存消耗:40.3 MB, 在所有 Java 提交中击败了5.00%的用户 */
public String intToRoman(int num) {
StringBuilder sb = new StringBuilder();
if(num < 1 || num > 3999) return "";
int n = 0;
if(num >= 1000){
n = num / 1000;
num = num - n * 1000;
for(int i = 0; i < n; i++) sb.append("M");
}
if(num >= 900){
sb.append("CM");
num = num - 900;
}
if(num >= 500){
sb.append("D");
num = num - 500;
}
if(num >= 400){
sb.append("CD");
num = num - 400;
}
if(num >= 100){
n = num / 100;
num = num - n * 100;
for (int i = 0; i < n; i++) sb.append("C");
}
if(num >= 90){
sb.append("XC");
num = num - 90;
}
if(num >= 50){
num = num - 50;
sb.append("L");
}
if(num >= 40){
sb.append("XL");
num = num - 40;
}
if(num >= 10){
n = num / 10;
num = num - n * 10;
for (int i = 0; i < n; i++) sb.append("X");
}
if(num >= 9){
sb.append("IX");
num = num - 9;
}
if(num >= 5){
num = num - 5;
sb.append("V");
}
if(num >= 4){
sb.append("IV");
num = num - 4;
}
if(num >= 1){
n = num / 1;
for (int i = 0; i < n; i++) sb.append("I");
}
return sb.toString();
}

不过这样写if太多了,篇幅太长,整理成数组会好很多:

    /**
执行用时:6 ms, 在所有Java提交中击败了52.61%的用户
内存消耗:40.6 MB, 在所有 Java 提交中击败了5.00%的用户
*/
public String intToRoman2(int num) {
StringBuilder sb = new StringBuilder();
String[][] list = {{"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"},
{"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"},
{"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"},
{"", "M", "MM", "MMM"}};
sb.append(list[3][num/1000]);
sb.append(list[2][num%1000/100]);
sb.append(list[1][num%100/10]);
sb.append(list[0][num%10]);
return sb.toString();
}

这里要注意数组第一位是空字符串,因为有个0,我写的时候忘了这茬,还是看来网友的解题思路才想起的,

那么这两个解题思路都是比较局限性的解题思路,看了题解里大神的分析,想起来原来这题适用贪心算法

具体实现方法:

/**
执行用时:7ms,在所有Java提交中击败了33.11%的用户
内存消耗:40.8MB,在所有Java提交中击败了5.00%的用户
*/
public String intToRoman3(int num) {
int[] nums = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
String[] strs = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};
StringBuilder sb = new StringBuilder();
int n = 0;
while(n < 13){
while(num >= nums[n]){
sb.append(strs[n]);
num -= nums[n];
}
n++;
}
return sb.toString();
}

代码的逻辑还是很好理解的,既然用到的贪心算法,趁热打铁去leetcode多搜几道

比如455.分发饼干

/*
执行用时 :8 ms, 在所有 Java 提交中击败了99.61%的用户
内存消耗 :41.8 MB, 在所有 Java 提交中击败了5.14%的用户
*/
public int findContentChildren(int[] g, int[] s) {
Arrays.sort(g);
Arrays.sort(s);
int l = s.length - 1;
int r = 0;
for(int i = g.length - 1; i >= 0; i--){
if(l < 0) return r;
if(g[i] <= s[l]){
r++;
l--;
}
}
return r;
}

这里使用贪心算法,思路就是在当前范围下先解决吃的最多的,然后递归,就可以了

leetcode还有一道中等难度的贪心算法,就是452.用最少数量的箭引爆气球

经过之前几道贪心算法的历练,我觉得首先要在题目中找到局部最优解,那有最优就有对比,所以首先找到比对的对象,像上面的分发饼干,比对的就是孩子的胃口,所以以胃口进行排序,先解决最大的,这道气球题,根据题目,是以x轴排序,那么对比的就是x轴上的位置,因为位置是从0开始的,所以从最小的开始是最优解

if(points==null||points.length==0)return 0;
int r=1;
Arrays.sort(points, Comparator.comparingInt(o->o[1]));
int end=points[0][1]; for(int i=0;i<points.length;i++){
if(points[i][0]<=end)continue;
r++;
end=points[i][1];
}
return r;

把气球按照x轴位置排序,从最左边的气球开始,把箭从最左边气球的最左边擦边过,这样射完所有能射的气球后,第二支箭就从剩下气球里最左边的开始射,以此往复。

从图上来看(这图从leetcode官方拷贝)

Leetcode 12,452,455-贪心算法-LMLPHP

从左边的绿球开始扎,那么从6射,可以射到两个绿+红,剩下的气球里最左边的是黄色的,那么从黄色的最右边也就是12射,可以射到两只,射完后气球就没了,那么箭数就是2。

贪心算法和动态规划有点像,不过简单很多。。。

05-28 13:52