POJ 1011 - Sticks (剪枝)

Sticks

Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 162067 Accepted: 38914
Description

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.
Input

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.
Output

The output should contains the smallest possible length of original sticks, one per line.
Sample Input

9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0
Sample Output

6
5

搜索枚举 + 剪枝
1.组合每一个等长木棒的时候,优先从长的开始搜索,能减少搜索状态量
2.记录上一次搜到的地方,不要从第一根重新开始搜,保证每个等长木棒构成是从长到短搜索的。避免重复搜索同一个长度的木棒
3.如果第一根都无法用上,一定是失败的,最后一根等长不必搜,因为加起来一定为总长度
4.组合出来的等长木棒的长度,一定是所有木棒总长度的约数,因此我们枚举约数搜索检验即可
5.等长木棒的长度一定不小于最长的木棒的长度,因此枚举的起点可以优化
等长木棒的长度一定不大于木棒的总和,因此枚举的终点是木棒的总和

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<cstring> 
using namespace std;

int a[100] = {},n,len;
bool check[100] = {};

bool dfs(int num,int x,int sum)//当前枚举到第几根了  剩下的需要拼凑的长度   剩下的总长度 
{
	if (x == 0)//如果一条木棒的长度凑齐了,就从头开始枚举木棒长度 
	{
		if (sum == 0)  return true;//如果已经拼凑完了全部木棒,则当前的长度可行 
		num = 1;
		x = len;
		while ((a[num] > x || check[num] != 0) && num <= n) num++;
		if (num <= n)
		{
			check[num] = 1;
			if (dfs(num+1,x-a[num],sum-a[num])) return true;//如果当前的策略已经成功 
			check[num] = 0;
			return false;
		}
	}	
	else
	{
		for (int i = num;i<=n;i++)
		{
			if (a[i] == a[i-1] && check[i-1] == 0)  continue;//如果前一根的长度跟当前这根一样,则没必要枚举这一根,因为前一根已经失败了 
			if (a[i] <= x && check[i] == 0)//如果当前木棒的长度大于剩下木棒的长度 或者 已经用过了 则略过 
			{
				check[i] = 1;//标记为已经用过 
				if (dfs(i+1,x-a[i],sum-a[i])) return true;
				check[i] = 0;
				if (x == len) return false;//如果第一根都无法用上,一定是失败的 
			}
		}
	}
	return false;
}

bool cmp(int i,int j){return i > j;}

int main()
{
	while (scanf("%d",&n) && (n != 0))
	{
		int tot = 0,max1 = 0;
		for (int i = 1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			tot += a[i];
			max1 = max(max1,a[i]);
		}
		sort(a+1,a+n+1,cmp);//优先从长的开始枚举搜索 
		memset(check,0,sizeof(check));
		for (int i = max1;i<= tot;i++)
		{
			if (tot % i != 0) continue; //木棒的长度一定是总长度的约数 
			len = i;
			if (dfs(1,i,tot))
			{
				printf("%d\n",i);
				break;
			}
		}
	}
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值