木棍还原(深度优先搜索)

这篇博客介绍了一道算法题目,涉及到深搜剪枝策略。小明有一些木棍,已被切割成长度不超过50的短棍,他需要恢复原始状态。题目要求设计程序找出最小的原始木棍长度,使得所有短棍能通过切割拼成原来的数量。算法思路包括从大到小排序木棍,然后从最大长度开始尝试,通过DFS遍历所有可能的组合,剪枝操作包括避免相同元素的重复遍历和提前结束枚举。提供的代码实现了这一策略,并在输入样例中成功找到了最小的原始长度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题描述 :

  小明有一些相同长度的木棍,他将它们随机切割,直到所有切割后的短棍长度不超过50。现在他想把棍子恢复到原来的状态,但他忘了他原来有多少根棍子(但知道至少有两根),也不知道它们原来有多长。请帮助他,设计一个程序,计算出这些棍子的最小原始长度。

输入说明 :

输入包含多组数据,每组数据包含2行。

第一行包含切割后的短棍的数量n,n<=64。

第二行包含这些短棍的长度,由空格分隔。所有长度值都是大于0的整数。

最后一组数据之后是一个0,表示输入结束。

输出说明 :

为每组数据输出一行,包括一个整数,表示棍子原始的长度。

如果原始长度有多种可能,则输出最小的那个。

输入范例 :

7
2 7 7 7 7 10 20
6
1 2 3 11 11 20
0

输出范例 :

30
24

算法思路:

这是一题深搜剪枝的题,剪枝操作主要体现在,将stick数组进行从大到小进行排序,可以加快算法速度,遍历所以可能的木棍的原始长度,这里的一个巧妙的操作是,从stick中最大的元素开始枚举,一直到所有stick数组元素的所有值的和,这样就减少了一些不必要的木棍原始长度的枚举了。

并且在dfs时,减少对相同元素的重复遍历。可以凑出目标长度时,退出枚举。

进行dfs操作时,结束条件是:组合的target长度的木棍的个数==stick总和/木棍目标长度

具体实现看代码注释。

代码实现:

#include<bits/stdc++.h>
using namespace std;
int n;
int stick[66];
int sum=0;
int visited[66];//标记是否枚举 
/*
target代表要求的长度
now代表现在所拼好的长度
count代表需要拼好的个数
x代表该次枚举的木棍编号 
*/ 
bool dfs(int x,int target,int now,int count)//x当前枚举的最小长度--now现在已经拼完的长度--sum现在拼完的根数  
{
    if(count==sum/target) //当拼好的木棍数==需要拼好的木棍数时 ,返回true 
	    return true;
    if(now==target)//当当前拼好的木棍长度达到所需木棍长度时,如果此时之后的小木棍可以拼出要求的木棍长度,返回true 
    { 
	    if(dfs(1,target,0,count+1)) //dfs深搜 下一个元素 
		return 1;
	} 
	
    for(int i=x;i<=n;i++)//枚举x之后的小木棍 
    {
	    if(!visited[i]&&now+stick[i]<=target)//下标为I的元素没有被访问过,并且现在已经拼接完的长度小于等于目标长度 
	    {
	        visited[i]=1;//标记访问过 
	        if(dfs(i+1,target,now+stick[i],count)) //进入下一次dfs(现在拼接的根数,下一个元素下标,目标长度,现在已经拼接的长度加上stick[i]木棍的长度,因为两者加载一起还没有超过target)
			    return true;
	        visited[i]=0;//回溯恢复标记值 
	        //剪枝操作 
	        if(stick[i]==target-now||now==0) //如果现在拼接的木棍的长度为0,或者此时截断的木棍长度==目标长度-现在拼接的木棍长度,跳出循环.可以凑出要求木棍长度时退出枚举 
			    break;
	        while(stick[i]==stick[i+1]) //排除相同项的重复遍历 
		    	i++;
	    }
	}
    
    return 0;
}
int main(){  
    cin>>n;
    for(int i=1;i<=n;i++)
    {
    	cin>>stick[i];//输入stick 
    	sum+=stick[i];//计算木棍总和 
	}
    sort(stick+1,stick+1+n,greater<int>());//从大到小进行排序 
    for(int i=stick[1];i<=sum;i++)//从断开的小木棍中的最大值开始遍历,一直到sum的长度,这是遍历所有可能的原始木棍的长度。 
    {
    	if(sum%i!=0)//如果不能整除,直接排除 剪枝操作,最后组装的木棍个数,肯定是总长度sum与木棍原始长度整除得到 
    	    continue;//直接跳过这个长度的枚举 
    	if(dfs(1,i,0,0))//深搜递归,下标从1开始,因为输入的时候就是从下标1开始存储木棍的长度的。 
    	{
    		cout<<i;//i is answer。 because we want a number which is smaller 
    		break;//跳出 
		}
	}
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值