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

问题描述 :

  小明有一些相同长度的木棍,他将它们随机切割,直到所有切割后的短棍长度不超过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;
}

  • 9
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
A:木棍还原问题是一种常见的深度优先算法问题。具体的思路是,先将破碎的木棍抽象成一个图形,然后从任意一根木棍开始,以逆时针的顺序将其端点遍历,并将遍历过的木棍断开,变成两根更短的木棍。然后从任意一条与当前木棍相邻的木棍再次开始遍历,直到所有的木棍都被拼接完成或者无法拼接。 具体步骤如下: 1. 选择一根木棍,从其中任意一端开始遍历它的邻接木棍。 2. 对于每个邻接木棍,重复1,直到所有邻接木棍被遍历过。 3. 如果当前木棍的两端都没有邻接木棍,则将其标记为已经访问,从当前栈中弹出并返回上一级调用。 4. 否则,选择一个未曾遍历的邻接木棍,将其作为当前木棍,将其标记为已访问并进入栈中,从1开始。 在实现深度优先算法时,需要注意以下几点: 1. 应该使用栈来维护当前遍历路径。 2. 应该记录每个木棍是否已经访问过,避免重复访问。 3. 应该检测当前木棍的邻接木棍是否能够拼接,如果不能,应该回溯到上一个节点尝试其他路径。 代码示例: ``` def dfs(stick, path): if len(path) == len(stick): # 所有木棍已经还原完成 return True for i in range(len(stick)): if not visited[i]: # 当前木棍未被访问 visited[i] = True path.append(stick[i]) for j in range(i+1, len(stick)): if not visited[j] and sum(path) + stick[i] + stick[j] <= total_length: visited[j] = True path.append(stick[j]) if sum(path) == total_length: # 可以完成拼接 return True if dfs(stick, path): # 搜索下一级节点 return True path.pop() visited[j] = False path.pop() visited[i] = False return False ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值