搜尋中的剪枝
阿新 • • 發佈:2018-11-02
一般來說剪枝有以下幾類
(1)優化搜尋順序
比如一些有多個物品然後湊重量的題就可以重量大的優先,根據重量排序
(2)排除等效冗餘
這是我最容易忽略的一點。在拼木棍那題中有淋漓盡致的體現
(3)可行性剪枝
如果當前無論如何都無法到達遞迴邊界就剪掉
(4)最優性剪枝
這個就很常見了,形如if(now >= ans) return
因為無論如何都無法有更優的答案
(5)記憶化
如果會重複遍歷狀態,那就可以用記憶化
幾道例題
poj 1011
http://poj.org/problem?id=1011
非常經典的一道題,拼木棍
列舉答案len,用搜索去判斷能不能拼成
(1)優化搜尋順序
長度從大到小排序
(2)排除等效冗餘
先用a再用b和先用b再用a是一樣的,所以可以規定從上一根木棍的下一根開始拼
如果一根木棍去拼不能拼成,那麼之後同樣長度的木棍一樣也不能,要剪去
如果當前已經拼了的長度為0,而又無法拼成,那麼剪掉,因為這個時候木棍的選擇是最多的,這個時候都不行,繼續下去木棍選擇變少更不行
#include<cstdio> #include<algorithm> #include<functional> #include<cstring> #define REP(i, a, b) for(register int i = (a); i < (b); i++) #define_for(i, a, b) for(register int i = (a); i <= (b); i++) using namespace std; const int MAXN = 100 + 10; int a[MAXN], vis[MAXN], len, n, num; bool dfs(int cnt, int last, int now) { if(cnt == num) return true; if(now == len) return dfs(cnt + 1, 1, 0); int fail = 0; //剪枝 _for(i, last, n) //剪枝 if(!vis[i] && now + a[i] <= len && fail != a[i]) { vis[i] = 1; if(dfs(cnt, i + 1, now + a[i])) return true; vis[i] = 0; fail = a[i]; if(now == 0) return false; ////剪枝 } return false; } int main() { while(~scanf("%d", &n) && n) { int sum = 0, maxt = 0; _for(i, 1, n) { scanf("%d", &a[i]); sum += a[i]; maxt = max(maxt, a[i]); } sort(a + 1, a + n + 1, greater<int>()); //剪枝 for(len = maxt; len <= sum; len++) //列舉答案 if(sum % len == 0) { num = sum / len; memset(vis, 0, sizeof(vis)); if(dfs(0, 1, 0)) break; } printf("%d\n", len); } return 0; }