蓝桥杯 Sticks

问题描述
  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.
  
输入格式
  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.
  
输出格式
  The output should contains the smallest possible length of original sticks, one per line.
  
样例输入
9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0
样例输出
5
6

该题可以用深度优先搜索算法解答,但算法若不加与优化就会超时,该题的难点与重点在算法优化上。算法优化可以通过剪枝来完成,即去除不必要的搜索。

代码如下:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int visited[100] = {0};
int num[100] = {0};
int n;

void sort(int *nums, int numsSize)//冒泡排序
{
	int i, j;
	int tmp;

	for (i = 0; i < numsSize; i++)
	{
		for (j = 0; j < numsSize - 1; j++)
		{
			if (nums[j] < nums[j + 1])
			{
				tmp = nums[j];
				nums[j] = nums[j + 1];
				nums[j + 1] = tmp;
			}
		}
	}
}
int dfs(int aver, int nowLen, int nowGet, int pos, int end)
{
	int i;

	if (pos >= n)//越界返回
		return 0;

	if (nowGet == end)//判断是否所有棍子都组合完
		return 1;
	
	for (i = pos; i < n; i++)
	{
		if (!visited[i])//查看当前棍子是否已经被组合
		{
			if (nowLen + num[i] == aver)//当前组合满足长度条件
			{
				visited[i] = 1;//标记已访问

				if (dfs(aver, 0, nowGet + 1, nowGet, end))//nowGet +1表示符合条件的组合多了一个
				//nowLen传入0表示开始下一个组合,长度清0
				//从nowGet开始表示第一个元素和后面元素组合满足条件后,从第二个开始组,以此类推,直到最后组完
					return 1;

				visited[i] = 0;//回溯

				return 0;
			}
			else if (nowLen + num[i] < aver)//如果当前的长度加上num[i]小于aver,就加上num[i]
			{
				visited[i] = 1;

				if (dfs(aver, nowLen + num[i], nowGet, i + 1, end))//i + 1表示和当前元素后面的元素组合,没必要再和前面的元素组
					return 1;

				visited[i] = 0;

				if (nowLen == 0)//nowLen = 0意味着后面的组合始终无法全部满足条件,此时可以直接返回,可以减少很多的时间
					return 0;

				for (; num[i] == num[i + 1] && i + 1 < n; i++);//如果后面的元素和当前元素一样,可以跳过,起到减少时间的效果
			}
		}
	}

	return 0;
}

int main()
{
	int i;
	int aver, sum = 0;

	while (scanf("%d", &n) && n)
	{
		sum = 0;

		for (i = 0; i < n; i++)
		{
			scanf("%d", &num[i]);
			sum += num[i];
		}

		sort(num, n);//对所有元素从小到大排序
	
		for (i = num[0]; i <= sum / 2; i++)//原始的棍子长度肯定大于num[0],所以i从num[0]开始递增,直到有i能满足条件
		{
			memset(visited, 0, sizeof(visited));//visited清零,很重要,因为会有多组输入,前一组输入的执行会改变visited,不清0会影响后面几组输入的执行
			if (sum % i != 0)//如果不整除,说明i肯定不行,跳过
				continue;
			else
			{
				if (dfs(i, 0, 0, 0, sum / i))//sum / i表示如果原始棍子的长度为i,一共有sum / i个棍子
				{
					printf("%d\n", i);//如果i符合,输出i
					break;
				}
			}
		}

		if (i > sum / 2)//如果i大于所有棍子和的一半,说明只有一个棍子,直接输出sum
			printf("%d\n", sum);
	}
		
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值