dfs之剪枝
作者:互联网
剪枝在dfs的应用——以切木棒问题为例
在dfs中,大致有如下几种剪枝方法:
-
优化搜索顺序
在搜索过程中,可以先从大的开始搜索。道理很简单,以填充空间为例,当我们先用体积大的物体填充时,空间会更快被填满,而剩余可选的决策数就少了。相反,如果从小物体开始,那么还会有很多空间,而可供选择的决策也有很多。 -
排除等效冗余
防止某个方案的重复出现,如对于某些组合,先选择A再选择B和先选择B再选择A的情况,这时我们可以认为的给每个设定一个顺序,使得A一定位于B之前。 -
可行性剪枝
对于某次搜索,当发现其已经失败后,就再无必要往下搜了。 -
最优性剪枝
一般用在求最优解问题,对于某次搜索,当发现本次搜索的值已经差于当前的搜到的最优解,就再无必要往下搜了。
木棒
题目描述:
乔治拿来一组等长的木棒,将它们随机地砍断,使得每一节木棍的长度都不超过 50 个长度单位。
然后他又想把这些木棍恢复到为裁截前的状态,但忘记了初始时有多少木棒以及木棒的初始长度。
请你设计一个程序,帮助乔治计算木棒的可能最小长度。
每一节木棍的长度都用大于零的整数表示。
代码如下:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 70;
int n, sum, len;
int w[N], vis[N];
/**
* now: 当前木棒以拼接的长度
* u: 搜索到了第几根木棒
* x: 从第x根木棍开始拼接木棒
**/
bool dfs(int now, int u, int x) {
if(u * len == sum) return true;
if(now == len) return dfs(0, u+1, 0);
// 剪枝2
for(int i = x; i < n; i++) {
// 剪枝3
if(vis[i] || now + w[i] > len) continue;
vis[i] = true;
if(dfs(now + w[i], u, i + 1)) return true;
vis[i] = false;
// 剪枝4
if(now == 0) return false;
// 剪枝5
if(now + w[i] == len) return false;
// 剪枝6
int k = i;
while(w[k] == w[i] && k < n) {
k++;
}
i = k - 1;
}
return false;
}
int main()
{
while(cin >> n && n) {
memset(vis, 0, sizeof vis);
sum = 0;
for(int i = 0; i < n; i++) {
cin >> w[i];
sum += w[i];
}
sort(w, w + n);
reverse(w, w + n);
for(len = 1; ; len ++) {
// 剪枝1
if(sum % len == 0 && dfs(0, 0, 0)) {
cout << len << endl;
break;
}
}
}
return 0;
}
以下是对代码各处剪枝的说明:
- 剪枝1: 木棒的总长度必然能够整除木棒的长度,否则该长度不合法
- 剪枝2:当拼接某根木棒时,给定一顺序,使得组成木棒的木棍从大到小递减,消除因木棍排列顺序不同带来的冗余。
- 剪枝3:当选择了某个木棍后木棒的长度超出给定的长度时,可直接跳过去枚举下根木棍。
- 剪枝4:当一个木棍放于开头却无法找到解时,则无论后面怎么排,都无法找到解了。因为任何一种组合都可以通过交换顺序使得该木棍位于开头,而这于上面矛盾了。
- 剪枝5:当一木棍被放在末尾而接下来却无法找到解时,则必然无解。即使我们拆掉最后一根木棍,也需要找到另外的若干根木棍来替代它,因此想通过改变最后一根木棍来达到目的是不可能。
- 剪枝6:当以根木棍不符合要求时,与它长度相等的木棍也不符合要求,这是显而易见的,每根长度相等的木棍的都是等同的,即使我们人为规定了顺序。
标签:剪枝,木棒,len,int,dfs,木棍,长度 来源: https://blog.csdn.net/PBomb/article/details/114303827