http://noi.openjudge.cn/——4.7算法之搜索——1819:木棒

题目

1819:木棒
总时间限制: 1000ms 内存限制: 65536kB
描述
乔治拿来一组等长的木棒,将它们随机地裁断,使得每一节木棍的长度都不超过50个长度单位。然后他又想把这些木棍恢复到为裁截前的状态,但忘记了初始时有多少木棒以及木棒的初始长度。请你设计一个程序,帮助乔治计算木棒的可能最小长度。每一节木棍的长度都用大于零的整数表示。
输入
输入包含多组数据,每组数据包括两行。第一行是一个不超过64的整数,表示砍断之后共有多少节木棍。第二行是截断以后,所得到的各节木棍的长度。在最后一组数据之后,是一个零。
输出
为每组数据,分别输出原始木棒的可能最小长度,每组数据占一行。
样例输入
9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0
样例输出
6
5

代码

#include <bits/stdc++.h>
using namespace std;
struct node{
	int x;
	int k;
	bool operator<(const node& h)const{
		return x>h.x;//降序 
	}
}s[70];
int n;
bool full(){//判定所有木棍都被选中 
	for(int i=0;i<n;i++)if(!s[i].k)return 0;
	return 1;
}
void view(int x) {
	cout<<"第"<<x<<"木棍"<<endl;
	cout<<"序号\t";for(int i=0;i<n;i++)cout<<i<<"\t";cout<<endl;
	cout<<"长度\t";for(int i=0;i<n;i++)cout<<s[i].x<<"\t";cout<<endl;
	cout<<"标记\t";for(int i=0;i<n;i++)cout<<s[i].k<<"\t";cout<<endl;
}
bool ok(const int& total,int he,int x,int id){//从第x木棍开始,升序凑够total
	if(full())return 1;//递归基,全选中就ok 
	if(total==he)return ok(total,0,0,id+1);//本次凑够了,但还有剩余木棍,从头下一轮 
	if(total<he)return 0;//超长了,本次失败 
	for(int i=x;i<n;i++){//遍历剩余木棍 
		if(s[i].k||he+s[i].x>total)continue;//剪枝,该木棍用过了,或者会超长 
		s[i].k=id;//标记 
		//view(i); 
		if(ok(total,he+s[i].x,i+1,id))return 1;
		s[i].k=0;//回溯 
		if(he==0)break;//剪枝,一个都没拼,就算了 
		while(i+1<n&&s[i].x==s[i+1].x)i++;//剪枝,代码走到这里说明该长度木棍用不成。 
	}
	return 0;//没成 
}
int main(){
	//freopen("data.cpp","r",stdin);
	while(cin>>n&&n){
		int sum=0,ans=0;//木棍总长,原始木棍最短长度 
		for(int i=0;i<n;i++){
			cin>>s[i].x;
			ans=max(ans,s[i].x);//找截后最长木棍(原始木棍起码是最长截后木棍)
			sum+=s[i].x;
			s[i].k=0;//多组数据,初始化 
		}
		sort(s,s+n);//降序排序 
		for(;ans<=sum;ans++){//从最长截取后长度遍历到木棍总长 
			if(sum%ans!=0)continue;//剪枝,木棍总长须被原始木棍(截取前均长)长度整除 
			if(ok(ans,0,0,1))break;//如果截取后木棍不同组合均能拼出ans,就是答案。 
		}
		cout<<ans<<endl;
	}
	return 0;
} 

结果

在这里插入图片描述

技术细节

从截取后最长木棍开始尝试,一直到所有木棍的和
木棍总长不能被单根原始木棍整除,不可取
递归:

  • 两个递归基:全都选完了,返回ok;超过原始木棍长度,返回失败;
  • 递归部分有两层,一层是:拼成后,从头(可以是第二个)拼下一组 。二层是:凑够本组。

凑够本组:

  • 遍历剩余木棍。
  • 剪枝:用过的不能用,超长的不能用。
  • 标记
  • 如果继续选后能拼成,才可以。(递归作为条件,前后拼成先后顺承)
  • 回溯取消标记

进一步剪枝:

  • 循环第一次后,当前木棍一根都没拼上,剩余不用循环,直接失败
  • 循环一次后,没有递归,就说明没有拼成,那往后一样的木棍也不用再试了。(可以放到前面剪枝,就说前面没选中,而且跟这根等长,也无需再试)

小结

递归要找
1.递归出口递归基
2.递归两个层面,接下一轮,和接下一根
3.回溯
4.剪枝

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值