1. 程式人生 > >POJ 1011 Sticks(搜索 && 剪枝 && 經典)

POJ 1011 Sticks(搜索 && 剪枝 && 經典)

closed play turn 深搜 span rem += 成功 curl

題意 : 有n根木棍(n<=64),它們由一些相同長度的木棍切割而來,給定這n根木棍的長度,求使得原來長度可能的最小值。

分析 : 很經典的深搜題目,我們發現答案只可能是所有木棍長度總和的因數,那麽我們只要去枚舉因數然後搜索是否可行即可!具體實現看代碼可能更容易看懂,這裏不贅述。重要的是體會此類深搜的代碼構建過程以及剪枝的考慮的巧妙性!

技術分享圖片
#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
const int maxn = 64 + 5;
bool vis[maxn]; int sticks[maxn]; int CurLen; int n; int Remain;///每一次都去重置為 bool DFS(int id, int Lack)///傳入的參數為上次選擇了哪個木棍、當前還缺多少才能組成一個新的目標木棍 { if(Lack == 0){///已經組成一個新的木棍了, Remain -= CurLen; if(Remain == 0) return true;///如果當前總長為0,說明此方案可行! int which; for(which=0; vis[which]==true
; which++);///由於是排好序,我們取第一個沒有使用過的木棍開始下一次的搜索 vis[which] = true;///標記 if( DFS(which, CurLen-sticks[which]) ) return true;///開始新一輪搜索 vis[which] = false;///記得標記回來 Remain += CurLen;///賦值回來!搜索題代碼應當註意的地方! }else{ for(int i=id; i<n; i++){ if(i>0 && (sticks[i]==sticks[i-1
] && !vis[i-1])) continue;///剪枝:排序後,如果有數字不能返回true,說明在此刻選 ///擇同樣的數字也是一樣的結果,所以跳過 if(vis[i] || Lack < sticks[i]) continue; Lack -= sticks[i]; vis[i] = true; if( DFS(i, Lack) ) return true; Lack += sticks[i]; vis[i] = false; if(sticks[i] == Lack) break;///剪枝:如果當前if成立,說明剛剛的DFS肯定是 ///到達過Lack==0的,既然不返回true,那麽說明 ///此方案不行,不必繼續往下面搜下去了! } } return false; } bool cmp(int fir, int sec){ return fir > sec; } int main(void) { while(~scanf("%d", &n) && n){ int tot = 0; for(int i=0; i<n; i++){ scanf("%d", &sticks[i]); tot += sticks[i];///累計總和 vis[i] = false; } sort(sticks, sticks+n, cmp);///排序方便剪枝 bool ok = false; for(int Len=sticks[0]; Len<=tot/2; Len++){ if(tot % Len == 0){ Remain = tot;///將Remain重置,代表當前搜索狀態下總長度還剩多少,如果為0說明成功 CurLen = Len;///全局變量記錄當前枚舉的是哪個因數,方便深搜操作 if(DFS(0, Len)){ printf("%d\n", Len); ok = true; break; } } } if(!ok) printf("%d\n", tot); } return 0; }
View Code

POJ 1011 Sticks(搜索 && 剪枝 && 經典)