木棒

木棒

乔治拿来一组等长的木棒,将它们随机地砍断,使得每一节木棍的长度都不超过50个长度单位。

然后他又想把这些木棍恢复到为裁截前的状态,但忘记了初始时有多少木棒以及木棒的初始长度。

请你设计一个程序,帮助乔治计算木棒的可能最小长度。

每一节木棍的长度都用大于零的整数表示。

输入格式
输入包含多组数据,每组数据包括两行。

第一行是一个不超过64的整数,表示砍断之后共有多少节木棍。

第二行是截断以后,所得到的各节木棍的长度。

在最后一组数据之后,是一个零。

输出格式
为每组数据,分别输出原始木棒的可能最小长度,每组数据占一行。

数据范围
数据保证每一节木棍的长度均不大于50。

输入样例:
9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0
输出样例:
6
5

要用搜索来解决问题,但是在本题中要进行剪枝就降低复杂度;
我们可以从小到大枚举长度len,len是所有木棍的和的约数,木棒数cnt就是sum/len
要进行四次剪枝:
1:优化搜索顺序,将木棍从小到大排序,
2:排出等效冗余:
(1):要求先后加入的木棍有单调性,因为先来一根长度为x的木棍,再来一个长度为y的木
棍,其实他们反过来是一样的,既然如此当然要有单调性.
(2):记录当前失败的木棒,如果当前失败了,下一次。见还是会失败 用fail记录
(3);如果第一根的木棒拼接就失败了,直接判定无解。因为这个分治必然也是失败的,因为在拼入这些木棍前,面对的原始木棍都是还没有拼接的,他们都是等效的.

#include <stdbool.h>
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
int a[100],v[100],n,len,cnt;
//正在拼stick根原始木棍
//第stick 根木棍的当前长度为cab
//拼接到第stick根木棍的上一根长度为last
int dfs(int stick ,int cab,int last)
{
	//所有原始木棍拼接好,成功
	if(stick>cnt)
	return 1;
	//第stick根木棒已经拼接好,去拼下一根 
	if(cab==len)
	return dfs(stick+1,0,1 );
	int fail=0;//记录最近的一次拼接失败的木棍,如果在下次的搜索中在遇到失败的木棍长度一样的,就不在搜索
    //小木棍长度递减,(从last开始)
	for(int i=last;i<=n;i++)
	if(!v[i]&&cab+a[i]<=len&&fail!=a[i])//fail!=a[i]进行剪枝,如果与失败的相同就不在搜索
	{
		v[i]=1;//标记已经用过的木棍
		if(dfs(stick,cab+a[i],i+1))
		return 1;
		fail=a[i];
		v[i]=0;//还原现场
		if(cab==0||cab+a[i]==len)//如果cab为0,或者相加正好是len,但是失败了,那么一定是失败了.
		return 0; 
	 } 
	 return 0;
 } 
int main() 
{ ios::sync_with_stdio(false);
	while(scanf("%d",&n)&&n)
	{
		int sum=0,val=0,m=0;
		for(int i=1;i<=n;i++)
		{
		    int x; scanf("%d",&x);
            if(x<=50)
            {
                a[++m]=x;
                val=max(val,a[m]); sum+=a[m];//求出最大的值,并求出所有的和
            }
		    
		    
		
		}
		sort(a+1,a+n+1);
		reverse(a+1,a+n+1);//从大到小排序
		for(len=val;len<=sum;len++)//从最大值开始枚举所有的长度
		{
			if(sum%len )//如果不能整除,就返回失败
			continue;
			cnt=sum/len;//原始木棍长度为len,共cnt根
			memset(v,0,sizeof(v));
			if(dfs(1,0,1))
			break; 
		}
		printf("%d\n",len);
	}
return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值