剪木棒 BFS深搜

题目描述

乔治采取了相同长度的棍子并随机剪断,直到所有部分的长度最多为50个单位。现在他想把棍棒还原到原来的状态,但是他忘记了原来他有多少棍棒,以及他们原来的时间。请帮助他,并设计一个程序,计算这些棒的最小可能的原始长度。所有以单位表示的长度都是大于零的整数。

输入

输入包含2行的块。第一行包含切割后的零件数量,最多有64个零件。第二行包含由空格分隔的部分的长度。该文件的最后一行包含零。

输出

输出应包含原始棒的最小可能长度,每行一个。

样例输入

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

样例输出

6
5


题目大意:将n节木棒接成m个长度相等的木条,要求木条的长度尽可能的短。

解题思路:DFS没什么好说的了,说一下几个剪枝的地方。

<1>首先,拼成木条的长度必须是所有木棒长度总和的约数,并且大于等于木条中的最大值。

<2>将木棒从打到小排列,可以减少递归的层数。

<3>当第i个木棒未选取时,如果i+ 1木棒与i木棒相同长度。跳过, 没有必要重复考虑。

<4>当遍历到i个木棒时,找不到小木棒和它组成木条时, 可以终止当前判断的木条长度。

算法实现:

#include <iostream>
#include <algorithm>
#include <string.h>
#define N 105
using namespace std;
int n,ans,sum,stick[N],vis[N];
int cmp(const int&a,const int&b)
{
	return a>b;
}
bool dfs(int cnt,int pos,int sum)//拼凑了几根木棒 当前进行拼凑木棒位置  木棒现在被拼凑后总长度 
{
	if(cnt==n) return true;//如果已经拼凑完了n根木棒 返回true 
	for(int i=pos;i<n;i++)//将当前木棒位置一直深搜到 最后一根木棒位置 
	{
		if(vis[i]) continue;//如果当前木棒已经被选取过 跳过 
		if(sum+stick[i]<ans)//如果 拼凑以后的总长度小于 原木棒长度 
		{
			vis[i]=1;//标记现在挑选的木棒 
			if(dfs(cnt+1,i+1,sum+stick[i]))//再进行深搜,如果成功 返回正确 
			return true; 
			vis[i]=0;
			while(stick[i]==stick[i+1]&&i+1<n) i++;//如果下一个木棒和当前木棒长度一致 则跳过 
		}
		else if(sum+stick[i]==ans)//如果拼凑以后的木棒等于 原木棒长度 
		{
    		vis[i]=1;//标记现在的木棒搜索过 
			if(dfs(cnt+1,0,0))//对剩下的木棒进行深搜 如果成功 返回成功 
			return true;
			vis[i]=0;//不成功的话 将现在的木棒标记为未搜索过 返回失败 
			return false;
		}
		if(sum==0)//如果找不到可以和当前长度木棒进行拼凑短木棒 可以直接判断返回失败 
		return false;
	}
	return false;//如果搜索完了 仍然不行则返回失败 
}
int main()
{
	while(cin>>n,n!=0)
	{
		memset(stick,0,sizeof(stick));
		sum=0;
		for(int i=0;i<n;i++)//sum进行木棒总长求和 
		{
			cin>>stick[i];
			sum+=stick[i];
		}
		sort(stick,stick+n,cmp);//将短木棒从大到小排序 
		for(int i=n;i>0;--i)//原来总木棒数目最多为n最少为1 
		{
			if(sum%i==0&&(sum/i)>=stick[0])//原来总木棒数一定会被总长整除  并且总长度一定会大于短木棒中最长的 
			{
				ans=sum/i;//ans用来记录原木棒长度 
				memset(vis,0,sizeof(vis));//将vis全部置为0,未拼凑过 
				if(dfs(0,0,0))//搜索 
				{
					cout<<ans<<endl;
					break;
				}
			}
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41486817/article/details/80706101