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:当以根木棍不符合要求时,与它长度相等的木棍也不符合要求,这是显而易见的,每根长度相等的木棍的都是等同的,即使我们人为规定了顺序。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值