深搜的剪枝技巧—小木棍

时间限制: 1 Sec  内存限制: 128 MB

题目描述

乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50。

现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。

给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。

输入

输入文件共有二行。

第一行为一个单独的整数N表示砍过以后的小木棍的总数,其中N≤60。

第二行为N个用空个隔开的正整数,表示N根小木棍的长度。

输出

输出文件仅一行,表示要求的原始木棍的最小可能长度。

样例输入

9

5 2 1 5 2 1 5 2 1

样例输出

6

题解:

可以依次枚举所有可能的长度len,用深搜判断是否能用截断后所有的小木棍拼出整数个len,找出最小的len

最优性剪枝:

1、设所有木棍的长度和是sum,那么原长度一定能够被sum整除,不然就没法拼了

2、木棍原来的长度一定大于等于所有木棍中最长的那根

可行性剪枝:

3、短小的木棍可以更灵活,可以对输入的木根从大到小排序

4、当用木棍i拼合时,可以从第i+1后的木棍开始搜索,因为根据优化1,前面的木棍已经用过了

5、用当前最长的木棍开始搜索,如果拼不出当前设定的原木棍的长度,则直接返回,换一个原始木根长度

6、相同长度的木棍不要搜索多次,用当前木棍的长度搜下去得不到结果时,用一支同样长度的还是得不到结果,所以,可以提前返回

7、判断搜到的几根木棍组成的长度是否大于原始长度len,如果大于,没必要搜下去,提前返回

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
int n,sum=0,a[60],len,m,temp,minn=0,used[60];
bool cmp(int x,int y)
{
	return x>y;
}
void read()
{
	cin>>n;
	for (int i=0;i<n;i++)
	{
		cin>>a[i];
		sum+=a[i];
    }
    sort(a,a+n,cmp);                 //剪枝3,从大到小排序,长木棍的灵活性差,先排长木棍 
}
void dfs(int k,int last,int rest)   //第k根木棍,last为第k根上一节木棍编号,rest为第k根木棍还需要的长度 
{
	if (k==m) {temp=1;return;}
	if (rest==0)                    //开始下一根木棍 
	{
		int i;
		for (i=0;i<n;i++)                       //剪枝4
		    if (!used[i]) {used[i]=1;break;}    //找到没有用过最长的木棍作为第一节 
		dfs(k+1,i,len-a[i]);
	}
	for (int i=last+1;i<n;i++)      //剪枝5、7,因为已经排好序了,所以它的下一个不大于它的长度 
	{
		int j;
		if (!used[i]&&rest>=a[i])   //木棍未被访问过,且比需要的木棍长度小
		{
			used[i]=1;
			dfs(k,i,rest-a[i]); 
			used[i]=0;              //回溯 
			j=i;                    //记录当前的木棍的编号 
			while (i<n-1&&a[i]==a[j]) i++;   //剪枝6,防止相同长度的木棍搜索多次 
			if (i==n-1)return ;               
		} 
	}
}
void solve()
{
	for (int i=a[0];i<=sum;i++)        //剪枝2
		if (sum%i==0)                  //剪枝1,sum能被原木棍的长度整除 
		{
			memset(used,0,sizeof(used));
			len=i;
			used[0]=1;
			temp=0;                    //记录有没有得到答案 
			m=sum/i;                   //木棍的根数 
			dfs(1,0,len-a[0]);         
			if (temp) {cout<<len<<endl;break;}
		}
}
int main ()
{
	read();
	solve();
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值