Poj 1011-Sticks

Poj 1011-Sticks

 

题意:有n根木棍(n<=64),它们由一些相同长度的木棍切割而来,给定这n根木棍的长度,求使得原来长度可能的最小值。

分析:这题不愧是经典中的经典,很早之前就写过,先后2次都没写出来,然后就放了一段时间,这是第三次了,终于写出来了。

基本思路是我们计算出出所有的木棍长度的长度和sum,从小到大枚举它的约数,找到第一个可行解就行了。

具体实现的话:首先,木棍有64根,搜索树是非常巨大的,如果不加任何剪枝,程序根本运行不出来,所以必须进行剪枝。

之前的处理办法是对于当前的木棍,搜索能与之搭配成功的木棍并标记,下次就不要考虑了,然而,这个方法是错误的,因为有时某根木棍能搭配的方法有多种,但是只有一种是可行的,而标记之后就错了。所以对于当前选取的第一个木棍,如果搭配成功了,但是没有得出解,则还是需要继续考虑不选取这些木棍的搜索。


剪枝的策略:

1、搜索的顺序的选取:首先对木棍做一次排序的的处理,越短的木棍越灵活,所以按木棍长度从大到小开始搜索,这样可以尽可能地剪枝。另外一点是排序之后也利于之后的可行性剪枝。

2、可行性剪枝:(1)、每次选定一根木棍去搭配之后,如果当前的木棍无法得出解,那么之后相同长度的木棍便不需要在重复搜索了,因为这也必定不可能得出解。

(2)、需要明确的一点是每根木棍一定是原来的一部分,这样的话如果选择某一根木棍开始搜索时,未能成功地得出解,那么没必要在进行后面的搜索了,直接返回即可。所以搜索时分开处理,如果是从0开始选取的第一根木棍,不成功就直接返回。

只需要应用上面的两个可行性剪枝,效率便可以大大提高,从而在规定时间内求出解。题目只需要求出可行解,而不是最优解,所以就不存在最优性剪枝了。

 

总结:搜索作为一个“万能算法”,虽说大部分情况可行,但是效率低下,一般搜索都要考虑到剪枝,而剪枝的技巧就很多了,首先考虑搜索的顺序,之后考虑两种方式的剪枝:可行性剪枝和最优性剪枝。可以多看看集训队的论文,总结全面而且总结的很好,但最重要的还是多实践,这样才能真正掌握其思想。

 

#include <cstdio>
#include <algorithm>
#include <functional>
#include <cstring>
using namespace std;

const int N = 70;
int a[N], vis[N];
int num, len, n;

bool dfs(int l, int cnt, int x) {
    if (cnt == num) return 1;
    if (l == 0) {//从0开始选择的第一根木棍,搜索失败直接返回
        int k = 0;
        while (vis[k]) k++;
        vis[k] = 1;
        if (dfs(a[k], cnt, k+1)) return 1;
        vis[k] = 0;
        return 0;
    }
    if (l == len) return dfs(0, cnt+1, 0);
    for (int i = x; i < n; ) {
        if (!vis[i] && l + a[i] <= len) {
            vis[i] = 1;
            if (dfs(l+a[i], cnt, i+1)) return 1;
            vis[i] = 0;
            int t = a[i];
            while (i < n && a[i] == t) i++;//相同长度的木棍不重复进行搜索
        }
        else i++;
    }
    return 0;
}
int main() {
    while (scanf("%d", &n), n) {
        int sum = 0, flag = 1;
        for (int i = 0; i < n; i++) scanf("%d", a+i), sum += a[i];
        sort(a, a+n, greater<int>());
        int t = sum/2;
        for (len = a[0]; len <= t && flag; len++) {
            if (sum % len == 0) {
                memset(vis, 0, sizeof(int)*n);
                num = sum/len;
                if (dfs(0, 0, 0)) { flag = 0; break; }
            }
        }
        printf("%d\n", flag ? sum : len);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值