POJ-1011: Sticks(dfs)

棍子
时限:1000 MS   内存限制:10000 K
提交材料共计:151760   接受:36143

描述

乔治拿出同样长的棍子,随意地剪,直到所有的部分都长到最多50个单位。现在他想把棍子还给原来的状态,但他忘记了原来有多少根棍子,也忘了原来有多少根棍子。请帮助他,并设计一个程序,以计算尽可能最小的原始长度的这些棍子。以单位表示的所有长度都是大于零的整数。

输入

输入包含2行块。第一行包含了切割后的棒件数量,最多有64根。第二行包含用空格分隔的部分的长度。文件的最后一行包含零。

输出量

输出应该包含最小的原始棒长度,每一行一个。

样本输入

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

样品输出

6
5
思路:

思路:dfs+剪枝。蛮经典的题目,重点在于dfs剪枝的设计。先说先具体的实现:求出总长度sum和小棒最长的长度max,则原棒可能的长度必在max~sum之间,然后从小到大枚举max~sum之间能被sum整除的长度len,用dfs求出所有的小棒能否拼凑成这个长度,如果可以,第一个len就是答案。

 

下面就是关键的了,就是这道题dfs的实现和剪枝的设计:

      1.以一个小棒为开头,用dfs看看能否把这个小棒拼凑成len长,如果可以,用vis[i]记录下用过的小棒,然后继续以另外一个小棒为开头,以此类推。

      2.小棒的长度从大到小排序,这个就不解释了。

      3.如果当前最长的小棒不能拼成len长,那么就返回前一步,更改前一步的最长小棒的组合情况(这里不能是全部退出),不用再继续搜索下去了。

      4.最重要的,就是比如说17,9,9,9,9,8,8,5,2……如果当前最长小棒为17,它与第一个9组合之后dfs发现不能拼成len,那么17就不用和后面所有的9组合了,而直接和8开始组合。这个剪枝直接从TLE到16MS,很强大。

include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int Max = 65;
bool flag, visit[Max];
int n, len, stick[Max];
void dfs(int dep, int now_len, int u) {
	if (flag)return;
	if (now_len==0) {
		int k = 0;
		while (visit[k])k++;
		visit[k] = true;
		dfs(dep + 1, stick[k], k + 1);
		visit[k] = false;
		return;
	}
	if (now_len == len) {
		if (dep == n)
		{
			flag = true;
		}
		else
			dfs(dep, 0, 0);
		return;
	}
	for (int i = u; i < n; i++)
	{
		if (!visit[i] && now_len + stick[i] <= len)
		{
			if (!visit[i - 1] && stick[i] == stick[i - 1])continue;
			visit[i] = true;
			dfs(dep + 1, now_len + stick[i], i + 1);
			visit[i] = false;
		}
	}
}
bool c(int x, int y)
{
	return x > y;
}
int main()
{
	while (cin >> n && n != 0)
	{
		int sum = 0;
		flag = false;
		for (int i = 0; i < n; i++)
		{
			cin >> stick[i];
			sum += stick[i];
		}
		sort(stick, stick + n, c);
		for ( len = stick[0];len <= sum; len++)
		{
			if (sum%len == 0) {
				memset(visit, 0, sizeof(visit));
				dfs(0, 0, 0);
				if (flag)break;
			}
		}
		cout << len << endl;
	}
	return 0;
}

 
  

猜你喜欢

转载自blog.csdn.net/usernamezzz/article/details/79893902