窮舉法的基本思想
窮舉法(枚舉法)的基本思想是:列舉出所有可能的情況,逐個判斷有哪些是符合問題所要求的條件,從而得到問題的全部解答。
它利用計算機運算速度快、精確度高的特點,對要解決問題的所有可能情況,一個不漏地進行檢查,從中找出符合要求的答案。
用窮舉算法解決問題,通常可以從兩個方面進行分析。
(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;
}