POJ1011 Sticks 深度优先搜索+剪枝

Sticks

Description

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 should 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跟不同长度的小棍拼接成若干跟长度相等的长棍,问能拼成的最短长度是多少。

分析:将木棍按长度降序排列(贪心),并计算总长度sum,则拼接的最短木棒长度ans一定在范围[ sticks[0],sum ]中,且一定有sum整除ans。若ans>sum/2则ans=sum。所以可以从小到大枚举符合条件的长度ans进行DFS,判断能否拼出该长度。

剪枝:

1、DFS从最长的木棒开始搜索,若能拼接成目标长度,则利用数组used标记为已用过,继续进行递归搜索新木棍;否则进行更换。若后续木棒无法组合出所需长度,则取消当前组合,重新排列。

2、每根木棍中的小木棒应从长到短排列,在搜索时,除了每根木棍中的第一条小木棒从序号0开始搜索,其余小木棒应从上一条木棒的序号之后开始搜索。

2、每根木棍中最长的木棒如果不能完成组合,则不用再考虑更换,直接更改前一个木棍的组合情况。

3、当前木棍长度len无法拼出所需长度时,跳过重复长度的木棍。

 

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=64;
int N,sticks[maxn],ans;
bool used[maxn];

bool cmp(int a,int b)
{
	return a>b;
}
/**
len:当前正在拼接的木棍已有的长度 
pos:下一个用于拼接的小木棒起始序号
left:剩余需拼接的木棍数量
**/ 
bool dfs(int len,int pos,int left)
{
	if(left==1) return true;//最后一根木棒不需搜索 
	
	for(int i=pos;i<N;i++)
	{//从上一条木棒的下一条pos开始搜索 
		if(used[i]||len+sticks[i]>ans) 
			continue; 
		
		used[i]=true;
		if(len+sticks[i]==ans)
		{//正好拼出所需长度 ,则开始考虑拼接新的木棍 
			if(dfs(0,0,left-1))
				return true;
			//新木棍不能拼接成功,取消标记 ,退出当前搜索 
			used[i]=false;
			return false;
		}
		else
		{//不够拼出所需长度,继续添加木棍 
			if(dfs(len+sticks[i],i+1,left))
					return true;
			used[i]=false;
			if(len==0) return false;//若为第一根木棒,不再考虑之前的组合 
			while(sticks[i+1]==sticks[i]) i++;//更换木棒时跳过重复长度的木棍 
		}
			
	}
	return false;
}

int main()
{
	while(cin>>N,N)
	{
		int sum=0;
		for(int i=0;i<N;i++)
		{
			cin>>sticks[i];
			sum+=sticks[i];
		}
		sort(sticks,sticks+N,cmp);//降序排列 
		for(ans=sticks[0];ans<=sum/2;ans++)
		{ //从最长小木棒开始搜索
			if(sum%ans) continue;
			memset(used,0,sizeof(used));
			if(dfs(0,0,sum/ans))
			{
				cout<<ans<<endl;
				break;
			}
		}
		if(ans>sum/2) cout<<sum<<endl;//ans直接取sum 
	}
    return 0;
}

 

 

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值