經典剪枝演算法的例題——Sticks詳細註釋版
阿新 • • 發佈:2019-02-16
這題聽說是道十分經典的剪枝算的題目,不要問我剪枝是什麼,我也不知道,反正我只知道用到了深度搜索
我參考了好多資料才悟懂,然後我發現網上的那些大神原理講的很明白,但程式碼沒多少註釋,看的很懵X,於是我抄起VS寫了個詳細註釋版,真的很詳細,史上最詳細,全宇宙最詳細,就這麼自信,不信你看,看不懂你咬我。
/*-------------------------------------------- * 剪枝演算法經典例題Sticks詳細註釋版 *---------------------------------------------*/ #include <iostream> using namespace std; //某根木棍被使用過就設定標誌位為1,沒使用過則設定標誌位為0 #define USED 1 #define UNUSED 0 #define MAXSIZE 64 //定義木棍結構體 typedef struct Stick { int length;//每根木棍的長度 int mark;//標誌位,記錄是否被使用過 }Sticks[MAXSIZE]; //雖然程式中使用全域性變數是不好的習慣,但做題who care int n;//未拼接時的總木棒數 int g;//拼接好後的木棒數 int len;//滿足要求的拼接後的木棒的長度 Sticks sticks;//定義一個木棒集合,記錄未拼接時各木棒的資訊 //定義一個氣泡排序演算法,從大到小排序 void BubbleSort(Sticks *a,int num) { int i,j; int temp;//交換時用的中間變數 for (i = 0;i<num-1;i++) { for (j = 0;j<num-1;j++) { if ((*a)[j].length<(*a)[j+1].length) { //交換 temp = (*a)[j].length; (*a)[j].length = (*a)[j+1].length; (*a)[j+1].length = temp; } } } } /*剪枝函式,深度搜索 *總共三個引數,nowLen表示現在拼接成的木棒的長度, *nowGet表示現在拼接成的木棒的總數,此值若等於之前的g則說明找到符合要求的木棒長, *cnt表示拼接過程中查詢剩下符合要求的木棒從哪個下標開始查詢,當cnt大於n時說明沒有符合要求的拼接方法 *該函式返回1代表找到了,0代表沒有找到符合要求的*/ int DFS(int nowLen,int nowGet,int cnt) { if (cnt>=n) return 0;//找的下標都超了,肯定不滿足 if (nowGet == g) return 1;//如果這個長度下獲取的總個數與g相等說明符合條件 int i; //開始遍歷查詢 for (i = cnt;i<n;i++) { if (sticks[i].mark==UNUSED) { //當找到的一組木棒恰好能拼接成需要的長度時 if (nowLen+sticks[i].length == len) { sticks[i].mark = USED;//設定這個木棒已使用過 //這組滿足,開始下一組尋找 if (DFS(0,nowGet+1,nowGet)==1) { //遞迴,直到最後每一組都滿足需要的長度時說明這個長度可行 return 1; } sticks[i].mark = UNUSED;//解除使用 return 0; } //當找到的一組木棒還小於拼接成需要的長度時 else if(nowLen+sticks[i].length < len) { sticks[i].mark = USED;//設定這個木棒已使用過 if(DFS(nowLen+sticks[i].length,nowGet,i+1)==1) { //同樣遞迴,這裡說明一下i+1,這個意思是從i+1下標開始尋找要使這組木棒滿足要求的木棒 return 1; } sticks[i].mark = UNUSED;//解除使用 //下面這句表示如果當前搜尋時,前邊的長度為0,而第一根沒有成功的使用, //說明第一根始終要被廢棄,所以這種組合必定不會成功 //此處的剪枝必須有,因為這裡的剪枝會節省很多的無用搜索, //缺少這一句超時 if (nowLen == 0) return 0; //下面這句是指如果有一根木棒加上去已經知道不滿足要求了,則與它相同長度的木棒都可以跳過 for ( ;sticks[i].length==sticks[i+1].length&&i+1<n;i++); } } } return 0; } int main() { int i; int sum;//木棒的總長 //使用者輸入每組的木棒數 while(cin>>n,n) { //木棒一開始都初始化為未使用過的 for (i = 0;i<MAXSIZE;i++) { sticks[i].mark = UNUSED; } sum = 0; for (i = 0;i<n;i++) { cin>>sticks[i].length; sum+=sticks[i].length; } //從大到小排序 BubbleSort(&sticks,n); for (len = sticks[0].length;len<=sum;len++) { if (sum%len!=0) continue;//最後選的木棒長度一定是能被總木棒長整除的 g = sum/len;//拼接的後的木棒數 //剪枝,滿足要求退出迴圈 if(DFS(0,0,0)) { break; } } //輸出滿足要求的木棒長 cout << len<<endl; } return 0; }