窮舉(一):窮舉法的基本思想


      窮舉是用計算機求解問題最常用的方法之一,常用來解決那些通過公式推導、規則演繹的方法不能解決的問題。采用窮舉法求解一個問題時,通常先建立一個數學模型,包括一組變量、以及這些變量需要滿足的條件。問題求解的目標就是確定這些變量的值。根據問題的描述和相關的知識,能為這些變量分別確定一個大概的取值范圍。在這個范圍內對變量依次取值,判斷所取的值是否滿足數學模型中的條件,直到找到全部符合條件的值為止。

      窮舉法(枚舉法)的基本思想是:列舉出所有可能的情況,逐個判斷有哪些是符合問題所要求的條件,從而得到問題的全部解答。

      它利用計算機運算速度快、精確度高的特點,對要解決問題的所有可能情況,一個不漏地進行檢查,從中找出符合要求的答案。

      用窮舉算法解決問題,通常可以從兩個方面進行分析。

      (1)問題所涉及的情況:問題所涉及的情況有哪些,情況的種數可不可以確定。把它描述出來。應用窮舉時對問題所涉及的有限種情形必須一一列舉,既不能重復,也不能遺漏。重復列舉直接引發增解,影響解的准確性;而列舉的遺漏可能導致問題解的遺漏。

      (2)答案需要滿足的條件:分析出來的這些情況,需要滿足什么條件,才成為問題的答案。把這些條件描述出來。

      只要把這兩個方面分析好了,問題自然會迎刃而解。

      窮舉通常應用循環結構來實現。在循環體中,根據所求解的具體條件,應用選擇結構實施判斷篩選,求得所要求的解。

      窮舉法的程序框架一般為:

cnt=0;                                   // 解的個數初值為0

for(k=<區間下限>;k<=<區間上限>;k++)          // 根據指定范圍實施窮舉 

   if (<約束條件>)                         // 根據約束條件實施篩選 

   { 

      cout<<(<滿足要求的解>);              // 輸出滿足要求的解 

      cnt++;                            // 統計解的個數 

   }

【例1】硬幣方案

      有50枚硬幣,可能包括4種類型:1元、5角、1角和5分。

      已知50枚硬幣的總價值為20元,求各種硬幣的數量。

      例如:2、34、6、8就是一種方案。而2、33、15、0是另一個可能的方案,顯然方案不唯一。

      編寫程序求出類似這樣的不同的方案一共有多少種?

      (1)編程思路。

      直接對四種類型的硬幣的個數進行窮舉。其中,1元最多20枚、5角最多40枚、1角最多50枚、5分最多50枚。

      另外,如果以元為單位,則5角、1角、5分會化成浮點型數據,容易計算出錯。可以將1元、5角、1角、5分變成100分、50分、10分和5分,從而全部采用整型數據處理。

      (2)源程序及運行結果。

#include <iostream>         

using namespace std;

int main()                           

{

    int a,b,c,d,cnt=0; 

    for(a=0;a<=20;a++) 

     for(b=0;b<=40;b++) 

      for(c=0;c<=50;c++) 

       for(d=0;d<=50;d++) 

          { 

          if(a*100+b*50+c*10+d*5==2000 && a+b+c+d==50) 

                { 

             cout<<a<<" , "<<b<<" , "<<c<<" , "<<d<<endl; 

             cnt++; 

                } 

          } 

    cout<<"Count="<<cnt<<endl; 

    return 0; 

      (3)程序的優化。

      上面的程序采用窮舉法求解,比較簡單。但在窮舉結構的設置、窮舉參數的選取等方面存在着改進與優化的空間。

一般來說,在采用窮舉法進行問題求解時,可從兩個方面來優化考慮。

      1)建立簡潔的數學模型。

      數學模型中變量的數量要盡量少,它們之間相互獨立。這樣問題解的搜索空間的維度就小。反應到程序代碼中,循環嵌套的層次就少。例如,上面的程序中,采用變量a、b、c、d分別表示1元、5角、1角和5分硬幣的枚數,對這4個變量窮舉,循環層次為4層。實際上這4個變量彼此間有兩個條件在約束,或者枚數等於50,或者總價值為20元。因此,可以只窮舉3個變量,另外一個變量通過約束條件求出,從而將循環層次減少為3層。

      2)減小搜索的空間。

      利用已有的知識,縮小數學模型中各個變量的取值范圍,避免不必要的計算。反應到程序代碼中,循環體被執行的次數就減少。例如,在窮舉時,先考慮1元的枚數a,最多為20枚(即0<=a<=20),再考慮5角的枚數b,若采用總價值不超過20元約束,則其枚數最多為(2000-a*100)/50枚(即0<=b<=(2000-a*100)/50),之后考慮1角的枚數c,其枚數最多為 (2000-a*100-b*50)/10(即0<=c<=(2000-a*100-b*50)/10)。這樣窮舉的循環次數會大大減少。

      采用上述思路優化后的源程序如下。

#include <iostream>         

using namespace std;

int main()                           

{

    int a,b,c,d,cnt=0; 

    for(a=0;a<=20;a++) 

     for(b=0;b<=(2000-a*100)/50;b++) 

      for(c=0;c<=(2000-a*100-b*50)/10;c++) 

          { 

          d=(2000-a*100-b*50-c*10)/5;    // 剩下的用5分硬幣填充

                if(a+b+c+d==50) 

                { 

             cout<<a<<" , "<<b<<" , "<<c<<" , "<<d<<endl; 

             cnt++; 

                } 

          } 

    cout<<"Count="<<cnt<<endl; 

    return 0; 

      也可以采用總枚數不超過50枚約束。先考慮1元的枚數a,最多為20枚(即0<=a<=20),再考慮5角的枚數b,則其枚數最多為(50-a)枚(即0<=b<=(50-a),之后考慮1角的枚數c,其枚數最多為 (50-a-b)枚(即0<=c<=50-a-b)。采用這種思路優化后的源程序如下。

#include <iostream>         

using namespace std;

int main()                           

{

    int a,b,c,d,cnt=0; 

    for(a=0;a<=20;a++) 

     for(b=0;b<=50-a;b++) 

      for(c=0;c<=50-a-b;c++) 

          { 

          d=50-a-b-c;    // 剩下的用5分硬幣填充

                if(100*a+50*b+10*c+5*d==2000) 

                { 

             cout<<a<<" , "<<b<<" , "<<c<<" , "<<d<<endl; 

             cnt++; 

                } 

          } 

    cout<<"Count="<<cnt<<endl; 

    return 0; 

}  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM