Uva307 Sticks 【dfs+剪枝】【习题7-14】

这篇博客介绍了如何解决UVA307 Sticks问题,这是一种利用深度优先搜索(DFS)和剪枝策略来确定最小原始木棍长度的方法。通过枚举木棍长度和数组位置,结合不同剪枝条件如长度为总长度的倍数、大于最大木条长度等,来优化搜索过程。博主强调了在判断标记数组时避免使用条件判断以提高效率,并提供了参考链接。
摘要由CSDN通过智能技术生成

题目Sticks

题意起初有一些相等的木棍,然后被人砍成了n个木条。长度分别给出,现在需将n个木条组合拼成回原来的木棍,问原始木棍的最小可能长度?

思路虽然知道用dfs,但没想到怎么实现多个相等的判断,然后参考了后做的,其实还是那些套路,就是变通不了。。。不过这个剪枝比较NB。。。

(1)枚举:枚举原始木棍长度,从总长度/i   i时n~1

(2)搜索:枚举给出的数组,枚举位置不是每次都从0开始,而成在递归中的pos参数,每次是从上一个的下一位开始的,递归中累加上木棍长度,当长度符合枚举的原始木棍长度时,再继续搜索,但是参数需要改变,pos从0开始,sum木棍长度也从0开始,当个数继续+1,当个数与n相等时所有所有木条都组合拼成木棍了。

(3)剪枝:

①递减排序,可以递归层数;

②枚举原木棍长度应是总长度的倍数且大于最达的木条长度,其他的没有必要搜索,因为根本达不到!

③当搜索时第i个木条时,第i个木条还未选(即放在还原标记数组的后面)时,如果后面的长度和i的相等,就没有必要再枚举,直接i++;(有点懵)

④当第i个木条没有找到时,没有必要再继续寻找了,直接return剪枝。(还是有点懵!)

注意:以后判断标记数组时用if(visit[i]) continue;  不能和有条件的判断中加 !visit[i] 这样时间会很慢!

参考JeraKrs博客

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 100 + 5;
bool cmp(int a,int b){return a > b;}
int n,stick[maxn],visit[maxn];
int ans,len;
bool dfs(int steps,int pos,int sum){
    if(steps == n) return true;//当到达第n个,说明所以木条都组合拼成了
    for(int i = pos;i < n;i++){
        if(visit[i]) continue;//关键,访问过的跳过
        if(sum + stick[i] < ans){
            visit[i] = 1;
            if(dfs(steps+1,i+1,sum + stick[i])) return true;
            visit[i] = 0;
            while(stick[i] == stick[i+1] && i+1 < n) i++;//剪枝:当i个数和第i+i数相等时,不需要再枚举,因为依然小于ans
        }
        else if(sum + stick[i] == ans){
            visit[i] = 1;
            if(dfs(steps+1,0,0)) return true;//当达到组合值时,将组合木条长度和下标位置归为0,继续搜索,因为是几个相等的木条,所以不是找到一个就行。。。
            visit[i] = 0;
            return false;
        }
        if(sum == 0) return false;//剪枝:当上面都没有return时,程序会走到这一步,说明第i个木条没有找到合适的组合,直接return 
    }
    
    return false;
}
inline int solve(){
    for(int i=n;i>=0;i--){//从大到小分
        if(len % i == 0 && len / i >= stick[0]){//总长度的倍数且平分长度大于初始的最长木条才可进行组合
            memset(visit,0,sizeof(visit));
            ans = len / i;//当前组合长度
            if(dfs(0,0,0)) return ans;
        }
    }
    return -1;
}
int main()
{
    while(scanf("%d",&n)!=EOF && n){
        len = 0;
        for(int i=0;i<n;i++){
            scanf("%d",&stick[i]);
            len += stick[i];
        }
        sort(stick,stick+n,cmp);//递减排序
        printf("%d\n",solve());
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值