1. 程式人生 > >搜尋中的剪枝

搜尋中的剪枝

一般來說剪枝有以下幾類

(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; }