密码箱是三位数的密码,也就是一共有1000种情况。
我曾经用枚举法找回忘记的密码,试了900多个才成功,大约10分钟。
今天在机房看到主机都是用密码锁锁起来的,仔细思考了一番,发现这里面蕴含着一些规律。
最普通的枚举法,自然是从000到999枚举了。
这种枚举法有2个规律
规律一:百位、十位、个位每次最多只变化1(从9到0也算变化1)
比如说,从9到10,百位不变,十位变化1,个位变化1
规律二:个位每次都变,即变化了999次,十位变化99次,百位只变化9次,一共是1107次
有没有变化更少的方案呢?
我想到了2048的策略,同样的,枚举密码也可以采用这种来回绕的方法
0 1 2 3 4 5 6 7 8 9 19 18 17 16 15 14 13 12 11 10
20 21 22 23 24 25 26 27 28 29 39 38 37 36 35 34 33 32 31 30
40 41 42 43 44 45 46 47 48 49 59 58 57 56 55 54 53 52 51 50
60 61 62 63 64 65 66 67 68 69 79 78 77 76 75 74 73 72 71 70
80 81 82 83 84 85 86 87 88 89 99 98 97 96 95 94 93 92 91 90
可以发现,对于这种枚举法,对于前面这些数来说,
每次都只有1位变化了1,另外2位不变。
如果后面是从100到190,200到290......900到990,这样的话,
只有在百位发生变化的时候才会有2位发生了变化。
所以,一共是发生了1008次变化。
计算变化次数的代码:
#include<iostream>
#include<iomanip>
using namespace std;
int f(int a, int b)
{
return (a % 10 != b % 10) + (a / 10 % 10 != b / 10 % 10) + (a / 100 != b / 100);
}
int map(int n)
{
if (n / 10 % 2 == 0)return n;
int s = n % 10;
return n - s + 9 - s;
}
int main()
{
int s = 0;
for (int i = 0; i < 999; i++)
{
cout << setw(4) << map(i);
s += f(map(i), map(i + 1));
}
cout << endl << s;
return 0;
}
这里的map就是一个计算数列通项的函数
结果果然也是1008
不过这还不是最好的结果。
最好的结果,是根据百位再度来回绕。
代码也很好改,只需要用复合函数就可以了
代码:
#include<iostream>
#include<iomanip>
using namespace std;
int f(int a, int b)
{
return (a % 10 != b % 10) + (a / 10 % 10 != b / 10 % 10) + (a / 100 != b / 100);
}
int map(int n)
{
if (n / 10 % 2 == 0)return n;
int s = n % 10;
return n - s + 9 - s;
}
int map2(int n)
{
if (n / 100 % 2 == 0)return map(n);
int s = n % 100;
return map(n - s + 99 - s);
}
int main()
{
int s = 0;
for (int i = 0; i < 999; i++)
{
cout << setw(4) << map2(i);
s += f(map2(i), map2(i + 1));
}
cout << endl << s;
return 0;
}
而且很有意思的是,map2和map差不多
运行结果:
很明显,这个就是变化最少的方案了,每2个数之间,都只有一位发生了变化。
当然,如果手动枚举密码的话,还是最普通的比较靠谱。