POJ 1011 Sticks 深搜+剪枝

【题意简述】: n根长度不同的小棍,拼成若干根长棍,要这些长棍的长度相等,并且小棍刚刚好都用完,问能拼成的长棍的最短长度是多少?


【思路】: 积累!!

首先 ,把小棍按长度,从大到小排序(为了进行贪心选择),并计算这些小棍的总长度,拼成的长棍的长度从最长

的小棍开始进行搜索,如果小棍的总长度能整除该长棍的长度,则可能完成拼凑。


进而 ,可以把问题花间成n根小棍,长度已知,拼成 num 根长度为 sum/num 的长棍,若能整除,便输出1,结束。 否则增加木棍的长度,继续搜索。最坏的情况下,所有小木棍拼在一起为一根长木棍!所以,无论怎样搜索总能

成功。


  由于过程中要确定某根小棍是否能被成功接收,它就得提前预知加入这根小棍后其他的小木棍能不能匹配成功,

 就要在遍历某个小木棍的时候,对其后的木棍进行递归搜索(深搜的特点),若能匹配成功,则标记当前小木棍为

用过,可以直接返回(试探成功) 


  如不能匹配,说明此木棍目前不可使用,将标记取消,待下一次搜索的时候再用。若当前木棍不可用,那么与

根小木棍长度相同的木棍自然也不可使用,直接将其跳过(剪枝)。

// 132k  0ms
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

#define maxn 70

int n,sum,aim,num,a[maxn];
bool used[maxn];
int cmp(int x,int y)
{
	if(x>y) return 1;
	else return 0;
}
bool dfs(int sticks,int len,int pos)
{
	//Stick表示当前组合好的木棍数,len表示已经有的长度,pos表示搜索到第几根
	int i;
	bool sign = (len == 0 ? true:false);
	if(sticks == num)                            //全部完成组合,搜索成功推出函数 
		return true;
	for(i = pos + 1;i < n; i++)
	{
		if(used[i]) continue;                    //棍子已经用过就不再用了 
		if(len + a[i] == aim)                    //正好完成组合 
		{
			used[i] = true;                      //标记第i根棍子被使用 
			if(dfs(sticks + 1, 0, -1))           //进入下一层搜索 
				return true;
			used[i] = false;                     //若下一层搜索失败则证明第i根不应使用, 
			return false;                        //所以要置false 
		}
		else if(len + a[i] < aim)                //长度不够 
		{
			used[i] = true;                      //标记第i根棍子被使用 
			if(dfs(sticks,len  + a[i],i))        //进入下一层搜索,已组合好的棍子长度变为len + a[i] 
				return true;
			used[i] = false;
			if(sign) return false;
			while(a[i] == a[i+1]) i++; //剪枝! 
		}
	} 
	return false;
}

int main()
{
	while(scanf("%d",&n)==1)
	{
		if(n==0) break;
		sum=0;
		for(int i=0;i<n;i++)
		{
			scanf("%d",&a[i]);
			sum+=a[i];                       //所有木棍的长度和是结果的上限 
		}
		sort(a,a+n,cmp);                     //对sticks的长度降序排列 
		for(aim=a[0];aim<=sum;aim++)         //枚举每一种可能的木棍长度,最长为 
		                                     //sum,最短为a[0]; 
			if(sum%aim==0){
				num = sum / aim;             //木棍的个数 
				memset(used,false,sizeof(used));//注意每次都要初始化!
				if(dfs(1,0,-1)){
					printf("%d\n",aim);
					break;
				} 
			}
		
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值