暴力破解与实用性原则:
1)暴力破解的第一原则:实用性。
2)枚举法。
3)逆向解法。
真题一 年龄谜题
美国数学家维纳(N.Wiener)智力早熟,11 岁就上了大学。 他曾在 1935-1936 年应邀来中国清华大学讲学。 一次,他参加某个重要会议,年轻的脸孔引人注目。 于是有人询问他的年龄,他回答说:“我年龄的立方是个 4 位数。我年龄的 4 次方是个6 位数。这 10 个数字正好包含了从 0 到 9 这 10 个数字,每个都恰好出现 1 次。”请你推算一下,他当时到底有多年轻。
解题思路:
假设年龄是在1-50岁之间,暴力破解便是将1-50全部算出再筛选。
1)计算1-50的立方以及4次方,剔除掉立方不是4位数的以及4次方不是6位数的结果。
很明显,在这里就已经可以看出答案是18岁。暴力破解原则就是要实用、高效、快速。
2)将年龄的立方以及4次方串起来,判断这10个数字是否正好包含了从0到9这10个数字,每个都恰好出现1次。
思路:判断0-9是否都出现在串中。
完整代码:
public class Main{
public static void main(String[] args){
for (int i=1; i<50; i++) {
int a = i*i*i;
int b = a * i;
if((a+"").length() != 4 || (b+"").length() != 6) //判断年龄的立方是否4位数以及4次方是否6位数
continue;
String s = "" + a + b;
System.out.println(i + ": " + s + " " + isAnswer(s));
}
}
//判断是否是答案
public static boolean isAnswer(String s){
for (int i=0; i<10; i++) {
if(s.indexOf(i+"") == -1) //0-9只要有一个不包含在s串中,直接返回false
return false;
}
return true;
}
}
结果:
真题二:古罗马数字(枚举法)
古罗马帝国开创了辉煌的人类文明,但他们的数字表示法的确有些繁琐,尤其在示大数的时候,现在看起来简直不能忍受,所以在现代很少使用了。 之所以这样,不是因为发明表示法的人的智力的问题,而是因为一个宗教的原因,当时的宗教禁止在数字中出现 0 的概念! 罗马数字的表示主要依赖以下几个基本符号:
I --> 1
V --> 5
X --> 10
L --> 50
C --> 100
D --> 500
M --> 1000
这里,我们只介绍一下 1000 以内的数字的表示法。
单个符号重复多少次,就表示多少倍。最多重复 3 次。
比如: CCC 表示 300 XX 表示 20,但 150 并不用 LLL 表示,这个规则仅适用于 I X C M。
如果相邻级别的大单位在右,小单位在左,表示大单位中扣除小单位。比如: IX 表示 9 ,IV 表示 4 ,XL 表示 40 , 49 = XLIX
更多的示例参见下表,你找到规律了吗?
I = 1
II = 2
III = 3
IV = 4
V = 5
VI = 6
VII = 7
VIII = 8
IX = 9
X = 10
XI = 11
XII = 12
XIII = 13
XIV = 14
XV = 15
XVI = 16
XVII = 17
XVIII = 18
XIX = 19
XX = 20
XXI = 21
XXII = 22
XXIX = 29
XXX = 30
XXXIV = 34
XXXV = 35
XXXIX = 39
XL = 40
L = 50
LI = 51
LV = 55
LX = 60
LXV = 65
LXXX = 80
XC = 90
XCIII = 93
XCV = 95
XCVIII = 98
XCIX = 99
C = 100
CC = 200
CCC = 300
CD = 400
D = 500
DC = 600
DCC = 700
DCCC = 800
CM = 900
CMXCIX = 999
本题目的要求是:请编写程序,由用户输入若干个罗马数字串,程序输出对应的十进制表示。
输入格式是:第一行是整数 n,表示接下来有 n 个罗马数字(n<100)。 以后每行一个罗马数字。罗马数字大 小 不 超 过 999。 要求程序输出 n 行,就是罗马数字对应的十进制数据。
例如,用户输入:
3
LXXX
XCIII
DCCII
则程序应该输出:
80
93
702
解题思路:
1、遍历罗马数字字符串的每一个字符,将每个字符所对应的十进制数相加起来。
2、因为存在着小单位在大单位左边这种情况,而且还不是所有的小单位放到大单位左边都成立。所以单单第一步是不够的,然而总结规律,这种特殊情况却少之又少:
IV:4 IX:9
XL:40 XC:90
CD:400 CM:900
因此,接下来就是直接枚举出这6种情况,对上述第一步所得出的sum进行补偿即可。
完整代码:
import java.util.Scanner;
public class Main{
public static void main(String[] args){
String[] romeNums = new String[n];
Scanner s = new Scanner(System.in);
int n = s.nextInt();
for(int i=0; i<n; i++){
romeNums[i] = s.next();
}
for(int i=0; i<n; i++){
System.out.println(romeNumToDec(romeNums[i]));
}
}
public static int romeNumToDec(String romeNum){
int sum = 0;
for (int i=0; i<romeNum.length(); i++) {
char c = romeNum.charAt(i);
if(c == 'I') sum += 1;
if(c == 'V') sum += 5;
if(c == 'X') sum += 10;
if(c == 'L') sum += 50;
if(c == 'C') sum += 100;
if(c == 'D') sum += 500;
if(c == 'M') sum += 1000;
}
//对sum进行补偿
if(romeNum.indexOf("IV") != -1) sum -= 2;
if(romeNum.indexOf("IX") != -1) sum -= 2;
if(romeNum.indexOf("XL") != -1) sum -= 20;
if(romeNum.indexOf("XC") != -1) sum -= 20;
if(romeNum.indexOf("CD") != -1) sum -= 200;
if(romeNum.indexOf("CM") != -1) sum -= 200;
return sum;
}
}
结果:
问题:如何保证输入的罗马数字是正确的呢?
解决:可以采用逆向解法。
1)将罗马数字转为十进制数字;
2)设计一个方法,将所得到的十进制反而再转化为罗马数字;
3)将转化来的罗马数字与之前的比较看是否一致。