poj 1011-小白算法练习 Sticks 从最简单的做起 dfs+剪枝

探讨通过已知切割后的木条片段,如何找出原始木条的最短可能长度。利用搜索算法验证哪些长度能够完全由片段组合而成。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Sticks
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 145712 Accepted: 34485

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
再测这组数据看看
9
15 11 8 8 8 4 3 2 1 结果:20

Sample Output

6
5

翻译:这也是学英语的一部分

乔治拿了等长度的木条,直至把这些木条分成不超过50的部分,现在他想还原这些木条,但他忘记了原来是那个和原来多长,请帮他找到木条可能的最小值


输入:包括两行,第一行:分成多少条小木条(最多64条)

     第二行:每条木条的长度

输出:一行,最小的可能的原木条长度

最初思路:

我们可以知道所有的原木条长度是一样的,而且>=5.我们知道分成小木条之后,这些小木条之和肯定是原木条长度的倍数,长度不会多出来。以

9
5 2 1 5 2 1 5 2 1
为例。sum=24  那么 只要24/X是整数且X越小越好 且X>=小木条最大的数,求出X就好了。

然而只要考虑这些就够了吗,结果是否定的。我一开始认为只要求满足的最小约数就好了,但是想一下,你算出来的最小约数真的能被这些小木条凑起来吗,万一你的约数是7,但你的小木条都凑不出7,那不就开玩笑了吗。所以怎么办?但是我们可以知道原木条的长度肯定是>=小木条最大值 && 原木条的长度是sum的约数

解决问题:

那我们肯定是要找匹配这些约数,看哪个最小的约数可以满足让这些小木条正好可以凑起来。我们肯定是要一个一个的找喽---索搜

我们最好从大到小的找(所以我们从大到小排序),这样可以减少运行量,那我们怎么样才算找到呢?我们先拿出第一个数,发现还差一点点,找第二个数,直至找到相等,如果没有相等,则这个约数不是要找的数。如果这k个数加起来是这个数,我们还要往下看,剩下的数还有没有加到这个约数。直至结束。那么结束条件是什么呢?我们每加上一个数,那么就设这个数列的长度+1,如果这个数列长度==原数列,那么我们说,确实是这个数。

代码:

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int vis[65];
int t[65]; 
bool flag;
bool cmp(int a,int b){
	return a>b;
}
//cur_len为当前的长度(如果有一组数==len[约数]会置0)
//strlen为数列的长度
//maxlen是小木条的数量(最后是数列长度==小木条数量-->成功)
//len是约数 
void dfs(int cur_len,int strlen,int index,int len,int maxlen){  
	if(flag) return;
	if(cur_len==len)
	{
		if(strlen==maxlen)
		{
			flag=true;
		}
		else
		{
			dfs(0,strlen,0,len,maxlen);	
		}
		return;
	}
	if(cur_len==0){
		int k=0;
		while(vis[k]) k++;
		vis[k]=1;
		dfs(t[k],strlen+1,k+1,len,maxlen);
		vis[k]=0;
		return;
	}
	for(int i=index;i<maxlen;i++)
	{
		if(vis[i]==0)
		{
			if(cur_len+t[i]<=len)
			{
				if(!vis[i-1] && t[i] == t[i-1]) continue; 
				vis[i]=1;
				dfs(cur_len+t[i],strlen+1,index+1,len,maxlen);
				vis[i]=0;
			}
		}
	}
}
int main(){
	int T=0;//小木条
	while(cin>>T&&T!=0){
		int sum=0;
		flag=false;
		for(int i=0;i<T;i++) //输入数据
		{
			cin>>t[i];    
			sum+=t[i];   
		}
		sort(t,t+T,cmp);    //排序
		int i;
		for(i=t[0];i<sum;i++) 
		{
			 if(sum%i==0) //从小到大遍历每个约数
			 {
			 	memset(vis,0,sizeof(vis));
			 	dfs(0,0,0,i,T);  //判断是否可以凑出这个长度
			 	if(flag) break; 
			 } 
		} 
		cout<<i<<endl;	
	} 
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值