1. 程式人生 > >for循環枚舉法,全排列+dfs,補充浮點數註意事項

for循環枚舉法,全排列+dfs,補充浮點數註意事項

div 改變 全排列 .com init 如果 黃金 vid 不用

  其實這個題目我一直沒想好應該叫什麽,就是在做藍橋杯的時候會遇到很多的題,給你一等式,abcdef...分別是1-9(||12||15)不重復問你有幾種方案?

  我之前一直都是用的for循環在做,聽說這叫什麽暴力破解還是枚舉法的。小白不是很懂這些。

  但是之前做過一道題,好像就是15個數的for循環寫的,一個是巨累(因為我用a!=b&&a!=c&&a!=....真的特別多而且容易寫錯),另一個是我記得我這道題寫了很久最後提交沒有分數,因為!!!超時了!!!

  真的特別氣,當時的我只會做一種題就是for循環的這個,我覺得自己要涼涼了,唯一會的題還超時了。所以痛定思痛,自己去網上down了全排列+dfs的代碼。

  恰巧這次的代碼特別合適,我竟然看懂了(我以前也百度過,但是不記得為什麽沒有看懂,堅信我們for循環可以打天下的一直沒有再去看),而且真的是要特別的誇一誇這一套代碼,因為我之後所以的這種題,靠的都是這一套代碼。特別省事,差不多可以算是我唯一的模板了。

  昨天的題有一道這樣的,然後就想把我會的所有的方法整理出來,當然,模板萬歲啊。但是如果有時間走兩套程序的話,算是檢查了,保證準確率。

 標題: 馬虎的算式

小明是個急性子,上小學的時候經常把老師寫在黑板上的題目抄錯了。

有一次,老師出的題目是:36 x 495 = ?

他卻給抄成了:396 x 45 = ?

但結果卻很戲劇性,他的答案竟然是對的!!

因為 36 * 495 = 396 * 45 = 17820

類似這樣的巧合情況可能還有很多,比如:27 * 594 = 297 * 54

假設 a b c d e 代表1~9不同的5個數字(註意是各不相同的數字,且不含0)

能滿足形如: ab * cde = adb * ce 這樣的算式一共有多少種呢?(滿足乘法交換律的算式計為不同的種類,所以答案肯定是個偶數。)

第一種方法:最簡單的for循環法。最起碼10以內很簡單不會超時,只要仔細就不會出錯

 1 public class Main {
 2 
 3     public static void main(String[] args) {
 4         //2.能滿足形如:ab * cde = adb * ce這樣的算式一共有 ?種  
 5                 int a,b,c,d,e;
 6                 int count=0;
 7                 for(a=1;a<10;a++){
 8                     for
(b=1;b<10;b++){ 9 for(c=1;c<10;c++){ 10 for(d=1;d<10;d++){ 11 for(e=1;e<10;e++){ 12 int x=(10*a+b)*(100*c+10*d+e); 13 int y=(100*a+10*d+b)*(10*c+e); 14 if(x==y&& 15 a!=b&&a!=c&&a!=d&&a!=e&& 16 b!=a&&b!=c&&b!=d&&b!=e&& 17 c!=a&&c!=b&&c!=d&&c!=e&& 18 d!=a&&d!=b&&d!=c&&d!=e&& 19 e!=a&&e!=b&&e!=c&&e!=d ){ 20 count++; 21 } 22 } 23 } 24 } 25 } 26 } 27 System.out.println(count); 28 29 } 30 31 }

ps:誰能想到,我第一次這個題寫錯了呢,因為我忽略了這句話,忘了判斷是否是不同的數字了。--->假設 a b c d e 代表1~9不同的5個數字(註意是各不相同的數字,且不含0)在一個是,我看到題目中說--->滿足乘法交換律的算式計為不同的種類,所以答案肯定是個偶數。我做的時候沒有判斷是否不同數字,所以答案是1007,所以,我看到這句話,更加確定了,我做的真對啊。我以為他這句話是在提醒我,*2啊*2啊。結果當然是錯的啦。他寫這句話應該就是單純的幫你減輕負擔啊,不用考慮交換律的問題,這樣出來多少就是多少了。

第二種方法:for循環法稍作改變。新建一個used數組,分別對應1 2 3 4.。。9然後用used數組的值標記是否使用過了。記得在這一輪的for循環最後釋放哦。

 1 public class Main {
 2 
 3     public static void main(String[] args) {
 4         //2.能滿足形如:ab * cde = adb * ce這樣的算式一共有 ?種  
 5                 int a,b,c,d,e;
 6                 int count=0;
 7                 int []used=new int[11];
 8                 for(int i=1;i<10;i++){
 9                     used[i]=0;//0標記沒有用過 1標記用過了
10                 }
11                 for(a=1;a<10;a++){
12                     used[a]=1;
13                     for(b=1;b<10;b++){
14                         if(used[b]==1)continue;
15                         used[b]=1;
16                         for(c=1;c<10;c++){
17                             if(used[c]==1)continue;
18                             used[c]=1;
19                             for(d=1;d<10;d++){
20                                 if(used[d]==1)continue;
21                                 used[d]=1;
22                                 for(e=1;e<10;e++){
23                                     if(used[e]==1)continue;
24                                     used[e]=1;
25                                     int x=(10*a+b)*(100*c+10*d+e);
26                                     int y=(100*a+10*d+b)*(10*c+e);
27                                     if(x==y){
28                                         count++;
29                                     }
30                                     used[e]=0;
31                                 }
32                                 used[d]=0;
33                             }
34                             used[c]=0;
35                         }
36                         used[b]=0;
37                     }
38                     used[a]=0;
39                 }
40                 System.out.println(count);
41 
42     }

這裏再備註一下,其實used直接建Boolean比較好,用false true,這樣if判斷的時候 就不用特意去寫了。我這個是因為 我一開始用的boolean 運行的時候有錯誤,當時做的很煩躁就直接改了int 。之後在別的裏面試了boolean完全可以沒有問題,可能是其他的地方不知道那裏報錯了。

第三種方法:全排列+dfs

我其實一開始忘了這個辦法,然後翻之前的筆記找到的。先寫一下筆記上的原題:

A+B/C+DEF/GHI=0,A-I為1-9不重復,求有幾種可滿足式子。(DEF=D*100+E*10+F...)

 1 public class Main {
 2     static int count=0;
 3     public static void main(String[] args) {
 4         // TODO Auto-generated method stub
 5         float a[]=new float[10];
 6         boolean visit[]=new boolean[10];
 7         dfs(a,visit,1);
 8         System.out.println(count);
 9     }
10     private static void dfs(float[]a,boolean[]visit,int num){
11         if(num==10){
12             if(judge(a)){
13                 count++;
14             }
15             return;
16         }
17         for(a[num]=1;a[num]<10;a[num]++){
18             if(visit[(int)a[num]]==false){
19                 visit[(int)a[num]]=true;
20                 num=num+1;
21                 dfs(a,visit,num);
22                 num=num-1;
23                 visit[(int)a[num]]=false;
24             }
25         }
26     }
27     private static boolean judge(float[]a){
28         float A=a[1];
29         float B=a[2]/a[3];
30         float C=a[4]*100+a[5]*10+a[6];
31         float D=a[7]*100+a[8]*10+a[9];
32         if(A+B+C/D==10){
33             return true;
34         }
35         return false;
36     }
37 
38 }

然後根據這個寫了這道馬虎的算式的代碼:

 1 public class Main {
 2     //2.能滿足形如:ab * cde = adb * ce這樣的算式一共有 ?種  
 3     static int count=0;
 4     public static void main(String[] args) {
 5         // TODO Auto-generated method stub
 6         float a[]=new float[6];//數組長度由題目中的變量個數決定abcde 5
 7         boolean visit[]=new boolean[10];
 8         dfs(a,visit,1);
 9         System.out.println(count);
10     }
11     private static void dfs(float[]a,boolean[]visit,int num){
12         if(num==6){//==前面數組長度
13             if(judge(a)){
14                 count++;
15             }
16             return;
17         }
18         for(a[num]=1;a[num]<10;a[num]++){
19             if(visit[(int)a[num]]==false){
20                 visit[(int)a[num]]=true;
21                 num=num+1;
22                 dfs(a,visit,num);
23                 num=num-1;
24                 visit[(int)a[num]]=false;
25             }
26         }
27     }
28     private static boolean judge(float[]a){
29         //這一塊根據題目自己寫
30         //1 2 3 4 5
31         //a b c d e
32         float A=a[1]*10+a[2];
33         float B=a[3]*100+a[4]*10+a[5];
34         float C=a[1]*100+a[4]*10+a[2];
35         float D=a[3]*10+a[5];
36         if(A*B==C*D){
37             return true;
38         }
39         return false;
40     }
41 
42 }

這個全排列真的挺好用的,而且記住這個模板在做題特別省時間!!而且可以說完全不用過腦子了。

因為為了統一,a[]我就延續用了float,因為這種題真的就是來來回回的*||/所以 用float更放心一些。

補充:

1.拿浮點數進行暴力破解很危險啊,因為浮點數計算有誤差!!!

double a =1.0;

double b=0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1;

輸出a,b;得到的值是 a->1.0 b->0.999999999999

所以!如果sysout(a==b);必然是false。

2.那麽怎麽去比較呢?

  1.比較a-b是否小於一個比較小的數

System.out.println(Math.abs(a-b)<1e-6);

  2.避開浮點數,等式兩邊同時擴大。

  eg:求a*2.3+b*1.9==82.3可以轉化成a*23+b*19==823

3.舉例:浮點數的運算可以通過變換形式進行

???1/4=0 ==> int/int=int 所以0.25=>(int)0.25=>0

所以,1/a+1/b+1/c=1循環求解的時候可以

  1.  1.0/a+1.0/b+1.0/c=1.0 =>這個方法就避免了上面說的int/int =int

  2. bc+ac+ab=abc  =>這樣一轉換,就和浮點數沒有關系了,就不會影響了

4.舉例:特殊值舉例

1 System.out.println(3/0);
2 System.out.println(3.0/0);

執行第一行,輸出:0不能為分母的英文,報錯

執行第二行,輸出:Infinity,即無窮大。

why???因為第二行 =>3.0是浮點數(double/float),所以0=>看作了一個0.00000000000....就是一個很很很小的數。3.0/一個很很很小的數===>無窮大

1      double a=3.0/0;
2         System.out.println(a+1);
3         System.out.println(a+a);
4         System.out.println(1/a);
5         System.out.println(1/(-a));
6         System.out.println(a-a);   

輸出為:

Infinity
Infinity
0.0
-0.0
NaN

NaN=>not a number,不是一個數值。類似於小學說的無意義。當然NaN+||-一個數也都=NaN

5.任意精度問題(當float 不夠->double,double不夠?)

舉例:有效數字100位(如1/6)

1     BigDecimal a=BigDecimal.valueOf(1).divide(BigDecimal.valueOf(6),new MathContext(100));
2        System.out.println(a);

輸出:0.1666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666667

正好做到這裏插一個黃金連分數的題當例題了。

技術分享圖片

 1 import java.math.BigDecimal;
 2 import java.math.MathContext;
 3 
 4 public class Main {
 5     public static void main(String[] args) {
 6         // TODO Auto-generated method stub
 7         BigDecimal bd = new BigDecimal(1);
 8         for (int i = 0; i < 10000; i++) {    
 9             bd = bd.add(BigDecimal.ONE);    
10 //          bd = BigDecimal.ONE.divide(bd, 100, BigDecimal.ROUND_HALF_DOWN);
11             bd = BigDecimal.ONE.divide(bd, new MathContext(100));  
12         }    
13         System.out.println(bd.toString());   
14     }
15 }

輸出:0.6180339887498948482045868343656381177203091798057628621354486227052604628189024497072072041893911375

10 11兩行都可以用,答案都是對的。

嗯嗯,終於完成今天的任務了。本來沒想寫關於浮點數的這些的,沒想到寫著寫著就這麽多了。啊,馬上10點了,溜了溜了。

for循環枚舉法,全排列+dfs,補充浮點數註意事項