穷举法的基本思想
穷举法(枚举法)的基本思想是:列举出所有可能的情况,逐个判断有哪些是符合问题所要求的条件,从而得到问题的全部解答。
它利用计算机运算速度快、精确度高的特点,对要解决问题的所有可能情况,一个不漏地进行检查,从中找出符合要求的答案。
用穷举算法解决问题,通常可以从两个方面进行分析。
(1)问题所涉及的情况:问题所涉及的情况有哪些,情况的种数可不可以确定。把它描述出来。应用穷举时对问题所涉及的有限种情形必须一一列举,既不能重复,也不能遗漏。重复列举直接引发增解,影响解的准确性;而列举的遗漏可能导致问题解的遗漏。
(2)答案需要满足的条件:分析出来的这些情况,需要满足什么条件,才成为问题的答案。把这些条件描述出来。
只要把这两个方面分析好了,问题自然会迎刃而解。
穷举通常应用循环结构来实现。在循环体中,根据所求解的具体条件,应用选择结构实施判断筛选,求得所要求的解。
穷举法的程序框架一般为:
cnt=0; // 解的个数初值为0
for(k=<区间下限>;k<=<区间上限>;k++) // 根据指定范围实施穷举
if (<约束条件>) // 根据约束条件实施筛选
{
cout<<(<满足要求的解>); // 输出满足要求的解
cnt++; // 统计解的个数
}
有些问题没有明确的区间限制,可根据问题的具体实际试探性的从某个数开始,增值穷举,对每一个数作一判断,若满足条件即输出结果,结束穷举。
下面为几个穷举法的经典案例及代码实现(Java)
【案例1】硬币方案
有50枚硬币,可能包括4种类型:1元、5角、1角和5分。
已知50枚硬币的总价值为20元,求各种硬币的数量。
例如:2、34、6、8就是一种方案。而2、33、15、0是另一个可能的方案,显然方案不唯一。
编写程序求出类似这样的不同的方案一共有多少种?
代码:
public void show_01() {
int coin_num = 50, count = 0;// 硬币数量
int num_yuan, num_5jiao, num_1jiao, num_fen;
for (num_yuan = 0; num_yuan < 50; num_yuan++)
for (num_5jiao = 0; num_5jiao < 100; num_5jiao++)
for (num_1jiao = 0; num_1jiao < 500; num_1jiao++)
for (num_fen = 0; num_fen < 1000; num_fen++) {
if ((num_yuan + 0.5 * num_5jiao + 0.1 * num_1jiao + 0.01 * num_fen) == 20) {
System.out.println(num_yuan + "," + num_5jiao + ","+ num_1jiao + "," + num_fen);
count++;
}
}
System.out.println("共有" + count + "种组合方式!");
}
【案例2】4位分段和平方数
一个4位自然数分为前后两个2位数,若该数等于所分两个2位数和的平方,则称为4位分段和平方数。例如,2025=(20+25)2。
编写程序求出所有4位分段和平方数。
代码:
public void show_02() {
int i, j, num, count = 0;
for (num = 1000; num <= 9999; num++) {
i = num / 100;
j = num % 100;
if ((i + j) * (i + j) == num) {
count++;
System.out.println(num);
}
}
System.out.println("共有"+count+"个这样的4位分段和平方数");
}
【案例3】三个三位数
将1、2、…、9共9个数分成三组,分别组成三个三位数,且使这三个三位数构成1:2:3的比例,试求出所有满足条件的三个三位数。
例如:三个三位数192、384、576满足以上条件。
代码:
//这三个变量之后的案例还会用到
private int[] v = new int[10];
private int[] vis = new int[10];
private int[] bk = new int[10];
//*******************************
public void show_03() {
int num, num2, num3, count = 0;
for (int i = 0; i < 10; i++)
v[i] = 0;
for (num = 100; num < 999; num++) {
num2 = 2 * num;
num3 = 3 * num;
if (num2 <= 999 && num3 <= 999) {
if (judge(num) && judge(num2)&& judge(num3)) {
System.out.println(num + "," + num2 + "," + num3);
count++;
}
}
for (int i = 0; i < 10; i++)
v[i] = 0;
}
System.out.println("共有" + count + "种组合");
}
private Boolean judge(int n) {
int i = n % 10;
int j = n / 10;
j = j % 10;
int k = n / 100;
if (v[i] != 0)
return false;
else
v[i] = 1;
if (v[j] != 0)
return false;
else
v[j] = 1;
if (v[k] != 0)
return false;
else
v[k] = 1;
return true;
}
【案例4】完美运算式
把数字1、2、…、9这9个数字填入以下含加减乘除与乘方的综合运算式中的9个□中,使得该式成立
□^□+□□÷□□-□□×□=0
要求数字1,2,…、9这9个数字在式中都出现一次且只出现一次。
代码:
public void show_04() {
int a, b, c, i, j, k;
for (a = 1; a <= 9; a++)
for (b = 1; b <= 9; b++)
for (c = 1; c <= 9; c++)
for (i = 12; i <= 98; i++)
for (j = 12; j <= 98; j++)
for (k = 12; k <= 98; k++) {
if (i % j == 0) { // i/j不为小数
for (int t = 0; t < 10; t++) { // 初始化数组
v[t] = 0;
}
int ab = (int) Math.pow(a, b);
if (_04judge1(a) && _04judge1(b)&& _04judge1(c)
&& _04judge2(i)&& _04judge2(j) && _04judge2(k)
&& (ab + i / j - k * c == 0)) {
System.out.println(a + "^" + b + "+"+ c
+ "/" + i + "-" + j + "*"+ k + "=0");
}
}
}
}
private Boolean _04judge1(int n) {
if (v[n] == 0) {
v[n] = 1;
} else
return false;
return true;
}
private Boolean _04judge2(int n) {
int i, j;
i = n % 10;
j = n / 10;
if (v[i] == 0) {
v[i] = 1;
} else
return false;
if (v[j] == 0) {
v[j] = 1;
} else
return false;
return true;
}
【案例5】神奇算式
由4个不同的数字,组成的一个乘法算式,它们的乘积仍然由这4个数字组成。
比如:210 x 6 = 1260,8 x 473 = 3784,27 x 81 = 2187都符合要求。
如果满足乘法交换律的算式算作同一种情况,那么一共有多少种满足要求的算式呢?
代码:
public void show_05() {
int cnt = 0;
for (int i = 1000; i <= 10000; i++) {
for (int a = 0; a < 10; a++) { // 初始化数组
vis[a] = 0;
}
if (judge2(i)) {
for (int a = 0; a < 10; a++) { // 数组赋值
bk[a] = vis[a];
}
for (int j = 1; j <= 98; j++) {
for (int a = 0; a < 10; a++) { // 数组赋值
vis[a] = bk[a];
}
if (i % j != 0) {// 要求能整除
continue;
}
int k = i / j; // 既得k,j两个数字
if (j > k) { // 由乘法交换律可得,除去重复项
continue;
}
if (judge1(j, k)) {
System.out.println(j + "*" + k + " = " + i);
cnt++;
}
}
}
}
System.out.println("共有" + cnt + "种");
}
Boolean judge1(int x, int y) {//判断数字组成是否与i组成相同,相同返回1
do {
if (vis[x % 10] == 0) {
return false;
}
vis[x % 10]--;
x /= 10;
} while (x != 0);
do {
if (vis[y % 10] == 0) {
return false;
}
vis[y % 10]--;
y /= 10;
} while (y != 0);
return true;
}
Boolean judge2(int x) {//判断i是否含重复数字,有则返回0,否则返回1
do {
if (vis[x % 10] != 0) {
return false;
}
vis[x % 10]++;
x /= 10;
} while (x != 0);
return true;
}
【案例6】排它平方数
小明正看着 203879 这个数字发呆。
原来,203879 * 203879 = 41566646641
这有什么神奇呢?仔细观察,203879 是个6位数,并且它的每个数位上的数字都是不同的,并且它平方后的所有数位上都不出现组成它自身的数字。
请找出具有这样特点的所有6位数。
代码:
public void show_06() {
int num = 0, count = 0;
// BigInteger x ;
double result;
for (int a = 1; a <= 9; a++)
for (int b = 1; b <= 9; b++)
for (int c = 1; c <= 9; c++)
for (int d = 1; d <= 9; d++)
for (int e = 1; e <= 9; e++)
for (int f = 1; f <= 9; f++) {
//数组赋值
for (int t = 0; t <10;t++) {
v[t] = 0;
}
if (judge3(a)&&judge3(b)
&& judge3(c)&&judge3(d)
&& judge3(e) && judge3(f)) {
num = a * 100000 +
b * 10000 + c * 1000 + d
* 100 + e * 10 + f;
result=num*num;//平方之后的结果
if (judge4(num)) {
System.out.println(num +
"平方的结果为:"+ result);
count++;
}
}
}
System.out.println("共有" + count + "个这样的6位数");
}
private Boolean judge3(int n) {
if (v[n] == 0)
v[n] = 1;
else
return false;
return true;
}
private Boolean judge4(double n)// 用于判断平方结果
{
long s_n = (long) n;
BigInteger num1 = BigInteger.valueOf(s_n);
BigInteger num2 = BigInteger.valueOf(s_n);
BigInteger b_n = num1;
BigInteger temp;
BigInteger t = num1.multiply(num1);// 两数相乘
do {
temp = t.remainder(BigInteger.valueOf(10));// t%10
System.out.println(" " + temp);
b_n =b_n.divide(BigInteger.valueOf(10));// b_n /= 10;
if (!judge3(Integer.valueOf(temp + "")))
return false;
} while (b_n != BigInteger.valueOf(0));
return true;
}