J - Sticks 经典dfs+剪枝

J - Sticks   HDU - 1455

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

题意:有一些长度相等的木棍,被小明切割成多个长度不同的部分,现在给你这些切割完以后的木棍的长度,求原先最短的木棍的长度。

n个木棍,接下来一行n个数字,代表n个木棍的长度。

分析:先将这些木棍的长度stick[i]从大到小排序,然后求出这些木棍的长度之和sum,则原先的木棍长度一定在stick[0]和sum之间,所以在此范围内搜索。并且,如果sum%i!=0,那么i所代表的长度一定不是原来木棍的长度。第39行还是不太懂。

注:原来木棍的长度都相等。

代码:

#include<iostream>
#include<cstdio>
#include<string.h>
#include<algorithm>
using namespace std;
int n,cnt,sum;//sum为所有木棍的长度之和,cnt=sum/i 
int l[70];
bool vis[70];
bool cmp(int a,int b)
{
	return a>b;
}
int dfs(int len,int count,int length,int pos)//要凑的长度,已经凑出来的数量,已经凑出来的长度,位置  
{
	if(len==sum)//要凑的长度等于sum,返回1 
		return 1;
	if(count==cnt)//已经凑出来的数量等于应该凑的数量,返回1 
		return 1;
	for(int i=pos;i<n;i++)
	{
		if(vis[i])//标记过,略过  
			continue;
		if(len==(l[i]+length))//等于要凑的长度  
		{
			vis[i]=1;
			if(dfs(len,count+1,0,0))//将已经凑出来的长度置位0,且位置从0重新开始  
				return 1;
			vis[i]=0;//若最终没凑出来,还原  
			return 0;
		}
		else if(len>(l[i]+length))//小于要凑的长度  
		{
			vis[i]=1;
			length+=l[i];//已经凑出来的长度加上当前的长度  
			if(dfs(len,count,length,i+1))
				return 1;
			length-=l[i];//若最终没凑出来,还原  
			vis[i]=0;
			if(length==0)
				return 0;
			//剪枝  
			while(l[i]==l[i+1])//若l[i]长度的不行,则所有长度等于l[i]的都不行  
				i++;
		}
	}
	return 0;
}
int main()
{
	while(cin>>n)
	{
		if(n==0)
			break;
		cnt=sum=0;
		for(int i=0;i<n;i++)
		{
			cin>>l[i];
			vis[i]=0;
			sum+=l[i];
		}
		sort(l,l+n,cmp);
		for(int i=l[0];i<=sum;i++)//原始的长度一定在最大值与sum之间  
		{
			if(sum%i)//不能整除,肯定凑不出,略过  
				continue;
			cnt=sum/i;
			if(dfs(i,0,0,0))
			{
				cout<<i<<endl;
				break;
			}
		}
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值