算法训练 Sticks

试题 算法训练 Sticks

资源限制
时间限制:1.0s 内存限制:999.4MB
  棍棒
时限: 1000MS 内存限制: 10000K
提交总数: 113547 接受: 26078
问题描述
  乔治拿了相同长度的木棍,随机切开,直到所有零件的长度最大为50个单位。现在,他想将木棍恢复到原始状态,但是他忘记了原来拥有多少木棍以及它们原本有多长时间。请帮助他,并设计一个程序,计算出那些棍子的最小可能的原始长度。所有以单位表示的长度都是大于零的整数。
输入格式
  输入包含2行的块。第一行包含裁切后的木棍零件数量,最多为64支木棍。第二行包含由空格分隔的那些部分的长度。文件的最后一行包含零。
输出格式
  输出应包含尽可能短的原始摇杆长度,每行一根。
样例输入
9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0
样例输出
5
6

/*--------------------------------------------
*     剪枝算法经典例题Sticks详细注释版
*---------------------------------------------*/
 
#include <iostream>
 
using namespace std;
 
//某根木棍被使用过就设置标志位为1,没使用过则设置标志位为0
#define USED 1
#define UNUSED 0
#define MAXSIZE 64
 
//定义木棍结构体
typedef struct Stick
{
	int length;//每根木棍的长度
	int mark;//标志位,记录是否被使用过
}Sticks[MAXSIZE];
 
//虽然程序中使用全局变量是不好的习惯,但做题who care
int n;//未拼接时的总木棒数
int g;//拼接好后的木棒数
int len;//满足要求的拼接后的木棒的长度
Sticks sticks;//定义一个木棒集合,记录未拼接时各木棒的信息
 
//定义一个冒泡排序算法,从大到小排序
void BubbleSort(Sticks *a,int num)
{
	int i,j;
	int temp;//交换时用的中间变量
	for (i = 0;i<num-1;i++)
	{
		for (j = 0;j<num-1;j++)
		{
			if ((*a)[j].length<(*a)[j+1].length)
			{
				//交换
				temp = (*a)[j].length;
				(*a)[j].length = (*a)[j+1].length;
				(*a)[j+1].length = temp;
			}
		}
	}
}
 
/*剪枝函数,深度搜索
*总共三个参数,nowLen表示现在拼接成的木棒的长度,
*nowGet表示现在拼接成的木棒的总数,此值若等于之前的g则说明找到符合要求的木棒长,
*cnt表示拼接过程中查找剩下符合要求的木棒从哪个下标开始查找,当cnt大于n时说明没有符合要求的拼接方法
*该函数返回1代表找到了,0代表没有找到符合要求的*/
int DFS(int nowLen,int nowGet,int cnt)
{
	if (cnt>=n) return 0;//找的下标都超了,肯定不满足
	if (nowGet == g) return 1;//如果这个长度下获取的总个数与g相等说明符合条件
	int i;
 
	//开始遍历查找
	for (i = cnt;i<n;i++)
	{
		if (sticks[i].mark==UNUSED)
		{
			//当找到的一组木棒恰好能拼接成需要的长度时
			if (nowLen+sticks[i].length == len)
			{
				sticks[i].mark = USED;//设置这个木棒已使用过
				//这组满足,开始下一组寻找
				if (DFS(0,nowGet+1,nowGet)==1)
				{
					//递归,直到最后每一组都满足需要的长度时说明这个长度可行
					return 1;
				}
				sticks[i].mark = UNUSED;//解除使用
				return 0;
			}
			//当找到的一组木棒还小于拼接成需要的长度时
			else if(nowLen+sticks[i].length < len)
			{
				sticks[i].mark = USED;//设置这个木棒已使用过
				if(DFS(nowLen+sticks[i].length,nowGet,i+1)==1)
				{
					//同样递归,这里说明一下i+1,这个意思是从i+1下标开始寻找要使这组木棒满足要求的木棒
					return 1;
				}
				sticks[i].mark = UNUSED;//解除使用
				//下面这句表示如果当前搜索时,前边的长度为0,而第一根没有成功的使用,
				//说明第一根始终要被废弃,所以这种组合必定不会成功
				//此处的剪枝必须有,因为这里的剪枝会节省很多的无用搜索,
				//缺少这一句超时
				if (nowLen == 0) return 0;
				//下面这句是指如果有一根木棒加上去已经知道不满足要求了,则与它相同长度的木棒都可以跳过
				for ( ;sticks[i].length==sticks[i+1].length&&i+1<n;i++);
			}
		}
	
	}
 
	return 0;
}
 
int main()
{
	int i;
	int sum;//木棒的总长
 
	//用户输入每组的木棒数
	while(cin>>n,n)
	{
		//木棒一开始都初始化为未使用过的
		for (i = 0;i<MAXSIZE;i++)
		{
			sticks[i].mark = UNUSED;
		}
		sum = 0;
		for (i = 0;i<n;i++)
		{
			cin>>sticks[i].length;
			sum+=sticks[i].length;
		}
		
		//从大到小排序
		BubbleSort(&sticks,n);
		for (len = sticks[0].length;len<=sum;len++)
		{
			if (sum%len!=0) continue;//最后选的木棒长度一定是能被总木棒长整除的
 
			g = sum/len;//拼接的后的木棒数
			//剪枝,满足要求退出循环
			if(DFS(0,0,0))
			{
				break;
			}
		}
		//输出满足要求的木棒长
		cout << len<<endl;
	}
 
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值