Description
X is a good number if after rotating each digit individually by 180 degrees, we get a valid number
that is different from X. A number is valid if each digit remains a digit after rotation. 0, 1, and
8 rotate to themselves; 2 and 5 rotate to each other; 6 and 9 rotate to each other, and the rest of
the numbers do not rotate to any other number.
Now given a positive number N, how many numbers X from 1 to N are good?
Example
Input: 10
Output: 4
Explanation:
There are four good numbers in the range [1, 10] : 2, 5, 6, 9.
Note that 1 and 10 are not good numbers, since they remain unchanged after rotating.
Note
N will be in range [1, 10000].
Solution 1(C++)
#define SAME 0 // 0, 1, 8
#define VALID 1 // 2, 6, 8, 9
#define INVALID 2 // 3, 4, 7
class Solution {
public:
int rotatedDigits(int n) {
int i, num, count = 0, is_valid[] = {SAME, SAME, VALID, INVALID, INVALID, VALID, VALID, INVALID, SAME, VALID};
bool found = false;
for(i = 2; i <= n; i++){
num = i; found = false;
while(num){
if(is_valid[num % 10] == INVALID) {found = false; break;}
if(is_valid[num % 10] == VALID) found = true;
num = num/10;
}
if(found == true) count++;
}
return count;
}
};
Solution 2(C++)
class Solution {
public:
// Digits: 0 1 2 3 4 5 6 7 8 9
int type[10] = {0,0,1,2,2,1,1,2,0,1}; // 'Same' 'New' or 'Invalid' numbers
int differentRotations[10] = {0,0,1,1,1,2,3,3,3,4}; // Cumulative: New number
int validRotations[10] = {1,2,3,3,3,4,5,5,6,7}; // Cumulative: Valid number
int sameRotations[10] = {1,2,2,2,2,2,2,2,3,3}; // Cumulative: Same number
int countRotations(std::string str, bool isNewNumber) {
int digit = str[0] - '0';
if(str.length() == 1) return isNewNumber ? validRotations[digit] : differentRotations[digit];
int ret = 0;
if(digit != 0) {
ret += validRotations[digit-1] * std::pow(7,str.length() - 1);
if(!isNewNumber) ret -= sameRotations[digit-1] * std::pow(3, str.length() - 1);
}
if(type[digit] == 1) isNewNumber = true;
if(type[digit] != 2) ret += countRotations(str.substr(1, std::string::npos), isNewNumber);
return ret;
}
int rotatedDigits(int N) { return countRotations(std::to_string(N), false); }
};
算法分析
这道题,有两种主要的解法,一种是暴力枚举,也就是Brute Force算法。另一种就比较复杂了,是动态规划算法。解法二的答案参考了一篇博客:A Ludic Fallacy - Rotating Digits (Leet Code 788)。别人这博客写的挺好的。
emmm,别人的博客说的其实挺详细的,如果我翻译一遍的话,一方面是太麻烦了,很多数学符号,另一方面,也没啥技术含量。所以,我在这里就做一点解释。
这个方法重点在于要看懂博客中的两个决策树(Decision Tree)。红色方块的就是旋转后的数字不是数字的,所以不用考虑。黄色与绿色的都是旋转后还是数字的,但是黄色的是旋转之后和原来的数字一样的,比如:0、1、8。 绿色的是旋转后与原来的数字不一样的,可以参考LED显示的数字:2、5、6、9。那么,如果一个数字按照题目的要求,每一位上的数字分别旋转180度,如果含有红色方框的数字就不用考虑了。只有含有黄色与绿色的数字才可以,这样算起来,就是”有效的数字“——valid number。但有效的数字不一定是”好的数字“——”good number”。因为题目要求好的数字是,旋转之后与原来的数字不一样。所以要想不一样,必须含有一个绿色方框的数字,也就是说,之前计算的有重复。重复的部分就是全部是黄色框的数字,这部分的数字不含有绿色数字,比如:1、18、118、108等等。所以,在结合组成的新的数字不能超过N。就可以算出答案了。
多一句嘴,我倒是没看出这里哪里和我理解的动态规划有关系的。有可能解法二中的四个数组的生成考虑到了“动态规划”的思想吧。但不得不说,这道题还不错。值得日后反复做一做。
程序分析
emmm。程序值得说的地方太多了。还是自己动手写一写最好。要说性价比最高的当然就是解法一了。