小木棍

本文探讨了一个有趣的数学问题——乔治的木棍谜题。乔治将一些等长的木棍随意切割,每段长度不超过50。现在他试图用这些碎片还原原始木棍,但忘记了初始状态。文章提供了一个编程解决方案,通过暴力搜索加剪枝算法,找出原始木棍的最小可能长度。

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

题目描述
乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过 50 。现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。

输入格式
第一行为一个单独的整数 N 表示砍过以后的小木棍的总数。 第二行为 N个用空格隔开的正整数,表示 N 根小木棍的长度。

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

样例输入
9
5 2 1 5 2 1 5 2 1
样例输出
6
数据范围与提示
1≤N≤601 \leq N \leq 601≤N≤60

暴力搜索+剪枝
暴力:用一个dfs(int last,int group,int now)last表示第group组已经用了多少长度(用短木棍拼),group表示已经拼成了多少组,接下来就很好搜索了,我这里用的是桶排序。now表示当前可以用的最大木棍

优化:
1.首先是边界优化,枚举长度的时候,可以从输入的短木棍的最大值到总和除以2,为什么?因为长度肯定是要比他们的最大值大呀!而长度最多只能到总和/2,因为至少要分2组,如果这堆短木棍都不行,就直接输出总和就行了。

2.现在开始dfs,可以按长度的大到小来枚举,因为短木棍比长木棍更灵活,用来在后面实在拼不出来的时候才用短的。

3.最重要的优化就是如果当前算了一轮以后,还是和刚开始的答案一样,那么就直接回溯就行了,因为当前这些木棍并没有用啊。这个优化是至关重要的。

4.now也是很重要的,每次dfs时都要更新。可以大大缩小搜索范围

#include<cstdio>
#include<cstring>
#define max(a,b) ((a)>(b)?(a):(b))
#include<cstdlib>
using namespace std;
int a[110],k,n,p,low;
void dfs(int last,int group,int now)
{
	if(last<0)return;
	if(group==k)
	{
		printf("%d\n",p);exit(0);
	}
	if(last==0)dfs(p,group+1,low);
	for(int i=now;i>=1;i--)
	{
		if(a[i]>0)
		{
			if(last-i<0)continue;
			if(group==k){printf("%d\n",p);exit(0);}
			a[i]--;
			dfs(last-i,group,i);
			a[i]++;
			if(last==p)return;
		}
	}
}
int main()
{
	int x,sum=0;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&x);
		if(x<=50)a[x]++,sum+=x,low=max(low,x);
	}
	for(int s=low;s<=sum/2;s++)
	{
		if(sum%s==0)
		{
			k=sum/s;p=s;dfs(s,0,low);
		}
	}
	printf("%d\n",sum);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值