NOI 4.7 搜索 1819:木棒

题目来源:http://noi.openjudge.cn/ch0407/1819/

1819:木棒

总时间限制1000ms    内存限制65536kB

描述

乔治拿来一组等长的木棒,将它们随机地裁断,使得每一节木棍的长度都不超过50个长度单位。然后他又想把这些木棍恢复到为裁截前的状态,但忘记了初始时有多少木棒以及木棒的初始长度。请你设计一个程序,帮助乔治计算木棒的可能最小长度。每一节木棍的长度都用大于零的整数表示。

输入

输入包含多组数据,每组数据包括两行。第一行是一个不超过64的整数,表示砍断之后共有多少节木棍。第二行是截断以后,所得到的各节木棍的长度。在最后一组数据之后,是一个零。

输出

为每组数据,分别输出原始木棒的可能最小长度,每组数据占一行。

样例输入

9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0

样例输出

6
5

来源

POJ 1011

 -----------------------------------------------------

思路

枚举+深搜+剪枝。为叙述方便,记分割后的木棒为“短木棒”,分割前的木棒为“原木棒”。整体思路是从下界开始枚举答案,对于每一个答案,用深搜剪枝判断其可行性。搜索的下界是所有短木棒中最长者的长度,上界是所有短木棒总长。这里可以将上界优化为总长的一半,因为如果总长一半以下的答案都不行,那不用枚举总长一半以上的答案,就能知道答案是总长。

深度优先搜索的函数dfs的两个参数分别是剩余未拼的短木棒数量和当前正在拼的原木棒剩余没有拼好的长度。

剪枝的总体思路是贪心,先用长的短木棒去拼,因为越短的短木棒越灵活。剪枝有3个:

1. 如果和当前使用的短木棒等长且序号靠前的短木棒没有被使用,说明这个长度的短木棒根本就用不了,该答案不可行(有点脑筋急转弯,不过没有这步剪枝也能过)

2. 如果对于一个新开的原木棒,目前没有使用的最长的短木棒用不了,则这根最长的短木棒永远用不了了,该答案不可行(最关键的剪枝)

3. 如果一根短木棒恰好抵消了原木棒的剩余部分长度,而继续深搜发现不行,则不用这个可以恰好抵消原木棒剩余长度的短木棒换用更短的短木棒肯定也不行,因为这样降低了拼接其他原木棒的灵活性(这点非常难想到,不过没有这步剪枝也能过)

-----------------------------------------------------

代码 

#include
#include
#include
#include
using namespace std;

int n = 0;									// 短木棒条数
int L = 0;									// 原木棒长度
int len[70] = {};							// 短木棒长度
bool used[70] = {};							// 短木棒是否用过

bool dfs(int m, int left)					// m: 剩余没有用的短木棒数, left: 当前原木棒还剩余的长度。返回是否搜到可行解
{
	int i;
	if (m==0 && left==0)					// 如果短木棒用完恰好原木棒拼成
	{
		return true;
	}
	if (left==0)							// 如果一根完整的原木棒拼成,就再来一根原木棒
	{
		left = L;
	}
	for (i=n-1; i>=0; i--)
	{
		if (!used[i] && len[i]<=left)
		{
			if (i> n)
	{
		if (!n)
		{
			break;
		}
		mymax = 0;								// 最长的短木棒的长度(枚举的下界)
		sum = 0;								// 短木棒的总长度(枚举的上界)
		for (i=0; i> len[i];
			sum += len[i];
			mymax = max(mymax, len[i]);
		}
		if (n==1)								// n=1的情况需单独考虑
		{
			cout << len[0] << endl;
			continue;
		}
		sort(len, len+n);						// 把短木棒按长度从小到大排序
		for (L=mymax; L<=sum/2; L++)			// 从最长的短木棒长度枚举到总长度的一半
		{
			if (sum%L)							// 原木棒的长度必须是总长度的因子
			{
				continue;
			}
			memset(used,0,sizeof(used));
			if (dfs(n,L))					// 如果可行
			{
				cout << L << endl;			// 输出结果
				break;						// 跳出循环
			}
		}
		if (L>sum/2)						// 一旦小于等于sum/2的数都不行,只能把所有短木棒拼成一个长度为总长的原木棒
			// 注意不能是L==sum/2+1, 因为可能mymax > sum/2+1, 直接就没进上面那个for循环
		{
			cout << sum << endl;
		}
	}
	fin.close();
#endif
#ifdef ONLINE_JUDGE
	int i,mymax,sum;
	while (cin >> n)
	{
		if (!n)
		{
			break;
		}
		mymax = 0;								// 最长的短木棒的长度(枚举的下界)
		sum = 0;								// 短木棒的总长度(枚举的上界)
		for (i=0; i> len[i];
			sum += len[i];
			mymax = max(mymax, len[i]);
		}
		if (n==1)								// n=1的情况需单独考虑
		{
			cout << len[0] << endl;
			continue;
		}
		sort(len, len+n);						// 把短木棒按长度从小到大排序
		for (L=mymax; L<=sum/2; L++)			// 从最长的短木棒长度枚举到总长度的一半
		{
			if (sum%L)							// 原木棒的长度必须是总长度的因子
			{
				continue;
			}
			memset(used,0,sizeof(used));
			if (dfs(n,L))					// 如果可行
			{
				cout << L << endl;			// 输出结果
				break;						// 跳出循环
			}
		}
		if (L>sum/2)						// 一旦小于等于sum/2的数都不行,只能把所有短木棒拼成一个长度为总长的原木棒
		{
			cout << sum << endl;
		}
	}
	return 0;
#endif
}


你可能感兴趣的:(NOI)