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

题目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 
#include 
#include 
#include 
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


你可能感兴趣的:(UVa,------搜索-------,DFS,+,剪枝,第7章,暴力求解法,acm,uva,解题思路)