HDU 1455 sticks(dfs+剪枝)

一道经典的dfs题。锻炼一下剪枝的能力。
链接:http://acm.hdu.edu.cn/showproblem.php?pid=1455
题面:
Problem Description

George took sticks of the same length and cut them randomly until all parts became at most 50 units long. Now he wants to return sticks to the original state, but he forgot how many sticks he had originally and how long they were originally. Please help him and design a program which computes the smallest possible original length of those sticks. All lengths expressed in units are integers greater than zero.
Input
The input contains blocks of 2 lines. The first line contains the number of sticks parts after cutting, there are at most 64 sticks. The second line contains the lengths of those parts separated by the space. The last line of the file contains zero.
Output
The output file contains the smallest possible length of original sticks, one per line.
Sample Input

9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0

Sample Output

6
5

题目的大概意思就是,原本有若干根长度相同的棍子,现在被砍成了很多段(每段不超过50个单位,一共不超过60段),现在给你砍过的棍子的数量和棍子的长度。叫你输出重组回来的每根长度相同的棍子的最小数量。

这个题涉及到一些剪枝;
1.最终的棍子长度一定是棍子总长度的因子(必须要除尽啊),没有这个会WA
2.如果某个长度的棍子用不上的话,和它长度相同的棍子自然也用不上。
3.如果第一根就不符合,就直接返回失败

解释在代码里

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
int n,a[1000],num;//a数组存放棍子长度
int vis[1000];//标记
bool cmp(int a,int b){//这是为了棍子长度从大到小排序。
    return a>b;       //原因是从长的棍子开始选择的灵活性要低一些
}
bool dfs(int len,int l,int cnt,int pos){//len是我选择的预期长度,l是现在已选的长度。
    if(cnt==num) return true;          //cnt是已选择的棍子个数,如果和目标个数相同就返回找到
        for(int i=pos;i<n;i++){        //pos是现在的位置,从现在的位置到n循环
        if(vis[i])                    //如果已经访问过,就进入下一次
            continue;
        if(len==l+a[i]){              //这里有两种情况,1.已选的长度+要选的==目标长度2.已选的长度+要选的<目标
            vis[i]=1;                //置为访问过
            if(dfs(len,0,cnt+1,0))   //如果从现在状态能找到就返回找到
                return true;
            vis[i]=0;               //记得改回来
            return false;          //一个重要的剪枝,没有直接tle
        }
        else if(len>l+a[i]){//第二种情况
            vis[i]=1;
            if(dfs(len,l+a[i],cnt,i+1))//和上面差不多
                return true;
            vis[i]=false;
                if(l==0)//如果第一根就不符合,就直接返回失败
                return false;
            while(a[i]==a[i+1])//如果某个长度的棍子用不上的话,和它长度相同的棍子自然也用不上。
                i++;
        }
    }
    return false;
}
int main(){
    while(scanf("%d",&n)!=EOF,n){
        int ans,sum=0;
        for(int i=0;i<n;i++){//读入并计算总长
            scanf("%d",&a[i]);
            sum+=a[i];
        }
        sort(a,a+n,cmp);//从大到小排序
        ans=sum;
        for(int i=a[0];i<=sum;i++){
            if(sum%i!=0)
                continue;
            num=sum/i;//找到可能的最终长度i和数目num
            memset(vis,0,sizeof(vis));
            if(dfs(i,0,0,0)){
                ans=i;
                break;
            }
        }
        printf("%d\n",ans);//输出
    }
return 0;
}

总结:多多做题,总结剪枝技巧,逻辑很重要

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值