PKU ACM 1011 Sticks

题目链接:Sticks 木棒

Description

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

Input

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

Output

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

Sample Input

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

Sample Output

6
5


主要思路:

首先确定使用深度优先搜索(DFS)算法,再对解空间剪枝进行优化。

开始自己设计的DFS函数很不合理,后来参考了国赛梦的博客 点击打开链接

这里主要采用了5个剪枝方法:

opt1:对输入数组进行由大到小的排序,这样求解的时候从最大数开始取,更容易得到解;

opt2:最终的解一定大于等于最小的木棒,小于等于所有木棒的和;

opt3:各节木棒长度的和一定是最终解的整数倍,因为原始木棒是等长的;

opt4:在DFS的过程中,如果当前木棒和前一个木棒长度相等,并且前一根木棒没有被使用,这说明此种长度的木棒不在此次遍历的解中,因此直接跳过;

opt5:(这个优化很重要,我一开始没做,导致TC中后两组数据TL)如果当前正在解第 i 根大木棒,遍历到第1根小木棒时(也是当前解空间中最长的小木棒),失败了。那么,直接回溯到去解第 i - 1 根大木棒。因为,最长的这根小木棒 + 所有剩余小木棒 求解失败了,就意味着在剩下这些小木棒中,只要包含这条最长小木棒,就无法得到解。


源代码:

#include <iostream>
#include <algorithm>
using namespace std;

int sticks[65];
bool used[65];
int n = 0;
int sum = 0;
int goal = 0;

int cmp(const int& a, const int& b)
{
   return a > b;
} 

bool DFS(int cur, int index, int cnt)
{
	if(cnt == sum / goal)
		return true;

	for(int i = index; i < n; i++)
	{
		if(i && !used[i-1] && (sticks[i] == sticks[i-1]))	continue;	//opt4
		if(used[i])	continue;
		if(sticks[i] + cur > goal)	continue;
		
		used[i] = true;
		if(sticks[i] + cur == goal)
		{
			if(DFS(0, 0, cnt + 1))
				return true;
			used[i] = false;
			return false;
		}

		if(DFS(cur + sticks[i], i + 1, cnt))
		{
			return true;
		}
		else
		{
			used[i] = false;
			if(cur == 0)	return false;	//opt5
		}
	}

	return false;
}

int main()
{
	while(1)
	{
		cin >> n;
		if(!n)	break;

		sum = 0;
		goal = 0;
		memset(sticks, 0, sizeof(sticks));
		for(int i = 0; i < n; i++)
		{
			cin >> sticks[i];
			sum += sticks[i];
		}

		sort(sticks, sticks + n, cmp);	//opt1

		for(goal = sticks[0]; goal <= sum; goal++)	//opt2
		{
			if(sum % goal != 0)	//opt3
				continue;
			memset(used, 0, sizeof(used));
			if(DFS(0, 0, 0))
			{
				cout << goal << endl;
				break;
			}
		}
	}
	return 0;
}


最后再给出一组 Test Case,也是北京大学ACM论坛上的,请大家参考:

测试用例:

9
15 3 2 11 4 1 8 8 8

6
6 2 2 4 8 8

5
1 1 1 1 1

2
1 1

4
2 2 9 9

3
1 2 3

64
40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40
40 40 43 42 42 41 10 4 40 40 40 40 40 40 40 40 40 40 40 40 40
40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40
40

7
49 48 47 46 45 44 43

7
3 4 5 5 5 5 13

7
2 7 7 7 7 10 20

6
1 2 3 11 11 20

7
63 2 44 12 60 35 60

9
5 2 1 5 2 1 5 2 1

4
1 2 3 4

64
32 32 32 32 32 32 32 32 32 32
32 32 32 32 32 32 32 32 32 32
32 32 32 32 32 32 32 32 32 32
32 32 32 32 32 32 32 32 32 32
32 32 32 32 32 32 32 32 32 32
32 32 32 32 32 32 32 32 32 32
33 33 31 31

64
40 40 30 35 35 26 15 40 40 40 40 40 40 40 40 40 40 40 40 40 40
40 40 43 42 42 41 10 4 40 40 40 40 40 40 40 40 40 40 40 40 40
40 25 39 46 40 10 4 40 40 37 18 17 16 15 40 40 40 40 40 40 40
40


45
15 3 2 11 4 1 8 8 8 15 3 2 11 4 1 8 8 8 15 3 2 11 4 1 8 8 8 15 3 2 11 4 1 8 8 8
15 3 2 11 4 1 8 8 8
0

结果:
20
10
1
1
11
3
1251
322
20
30
24
276
6
5
64
454
20



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值