uva 307 Sticks

12 篇文章 0 订阅
6 篇文章 0 订阅

题目地址:

http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=243

题目描述:

Sticks 

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 file contains blocks of 2 lines. The first line contains the number of sticks parts after cutting. The second line contains the lengths of those parts separated by the space. The last line of the file contains zero.

Output

The output file 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

题意:给出一堆数字,要你将这堆数字通过加和 组成 最小的 数值都相等的新数字,必须每个数字都要相同 不许有冗余。

题解:DFS+剪枝,不得不佩服uva的测试数据之严格,碉堡天。此题的剪枝也相当考察构造剪枝策略的水平。

剪枝:

1、对于目标值的取值范围肯定是 介于 input 数组中 最大的和数组和之间的。我们把Min作为目标值,parts[]作为输入数组  即max(pars[i])<=Min<=sum(parts[i])

2、sum(parts[i]) 肯定要除得尽 Min值 才行

3、对parts数组排序 可以减低搜索工作量,这在我的uva 197 cube中 也讲到了,即我们先取数量大的搜索,这样整个情况或者说状态的可确定性就增加了,对于搜索的判断是否可行就可以降低工作量。

4、关键剪枝:如果我们在取parts[i]时  发现 之前的preparts  和  我们现在正要取的 parts[i]相同,而且之前的preparts还没有被我们选上,说明,parts[i]这个值  终究 不被我们选上,preparts就是前车之鉴(即在取preparts时 我们已经DFS过证明组合这个值是不可行的,所以后面遇到相同值肯定还是不行,直接剪掉)。

5、关键剪枝:在我们取组合成Min木条的 第一块小木快时 比如是parts[i]。如果parts[i]递归回来后,发现不可行,即退出这个Min木条的整个搜索。回到上一个Min木条的末端,尝试其他小木快来组成上一个Min木条。因为当parts[i]  DFS回来,不可行,表示  pars[i] 之后的那些木条  无论怎么和parts[i]组合 都是不可行的,如果我们不return,继续搜索,即这个parts[i] 留到后几层DFS用到,反正肯定要用到这个parts[i]。在小木快多的情况下(小木快是组合成Min的关键)与parts[i]尚且无法组合,在后几层的DFS中,小木快变少的情况下更不可能和parts[i]组合了。

6、关键剪枝:第5条说到小木快是组成Min的关键,因为经过排序,理想的组合是,大的木块(排序后 ,前端的parts[i])中 与 大的碎片(排序后, 后端的较小的parts[i])相组合,如果大的木块和 小的碎片相组合了,那么很有可能 出现 小的木块与 剩下的大的碎片 组合 会超过Min值  即不能组合出刚好是Min值的情况。

这里举个例子:(也是当初自己WA的情况)

10

21 14 13 11 9 6 4 3 2 1

如果我们纯粹贪心(感觉有个局部贪心可以做出来,思考中 orz...)按大的与大的组合会出现{21} {14,6,1}{13,4,3,?} 到13时  我们初衷是想组成21这个数,但是最小的碎片(1) 却已经与较大的木块(14)组合过了,所以这样21就组合不出来。如何我们这样组合:{21}{14,4,3}{13,6,2}{11,9,1}这样就能完整地组合出Min为21的情况。

所以我们要尽量保证大的木块与大的碎片组合,不能浪费了或者滥用了  将来要用来组成小的木块的小碎片。

所以,当我们在取parts[i],正好组合成一个Min时,如果DFS回来不可行,说明这个末端的parts[i]之后的那些小碎片不能组合成整体可行的情况(并不是指parts[i]不能组合),那我们return,剪掉这种情况。因为末端的较大的parts[i]刚好能组合Min,而剩下的小碎不论怎样都不能组合Min,如果我们不return 继续DFS,那么这个parts[i]的大碎片的位置被多于一块的小碎片来填补Min的空缺,然后DFS进去,parts[i]留到后面几层DFS,用一块大碎片 换出 几块 小碎片,本来纯粹的小碎片组合都不能组合成可行的情况,而现在剩余的这个碎片集合(大碎片parts[i]+其余的小碎片)更不能组合成可行的情况了(即不能与较小的木快 组合成Min值)。

代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <iostream>
#include <algorithm>
using namespace std;
int n=0;//the number of the parts
int parts[1000+5]={0};
bool visp[1000+5]={false};
int Min=0; //max(parts[i])<=min<=sum(parts[i])
int Sump=0;//the sum of the parts array
int numN=0;//the number of the original sticks
int flag=0;//flag =1 means it is suceessful to recover the sticks
/*compare*/
bool cmp(int a,int b)
{
	return(a>b);
}
/*dfs the sorted parts,multiple parameter 's DFS  means multiple recursive status var*/
int removenum=0;
int DFS(int sum,int num,int index)
{
	if(flag)
	{
		return(0);
	}
	if(num==numN)
	{
		flag=1;
	}
	int i=0;
	int preNo=-1;
	for(i=index;i<=n-1;i++)
	{
		if(!visp[i]&&preNo!=parts[i])//key prune:if the program can reach it ,that preNo==parts[i] then the preNo must be not conbined,because if can combine  the falg=1  then return this DFS path very early
		{
			preNo=parts[i];
			if(sum+parts[i]<Min)
			{
				visp[i]=true;
				DFS(sum+parts[i],num,i+1);//if bit pars[i] had been choosed then we pick smaller pars[i] to combine or make up to a Min length
				if(flag)
				{
					return(0);
				}
				visp[i]=false;
				//key prune: this first big sticks(to be conbine to the Min) can not be conbine to let the num==numN,so if we continue DFS,we at the after we will still use this stick,and the avalaible small sticks that we can use  would be more less than now DFS.so we prove it is not lead to num==numN in this DFS and it's many enter path,then we return it (bit path).
				if(sum==0)
				{
					return(0);
				}
			}
			else if(sum+parts[i]==Min)
			{
				visp[i]=true;
				DFS(0,num+1,0);//find a new combination let the 0 to a  Min
				if(flag)
				{
					return(0);
				}
				visp[i]=false;
				//key prune:if this Min complete last big sticks(to be conbine to the Min,note that the parts[] is sorted) can not let the flag=1(it can reach it says falg=0),then we return it. because if not return, we will find the more multiple smaller(not same the first prune,we can not pick the same pars[i]==pre) sticks to conbine the Min,but this big we have not used,in this DFS we used the this big,and left other small sticks can not let nun==nbumN,so, we use more multiply small to combine this ,and big left morely can not let the num==numN.
				//front big sitcks morely conbine rear small sticks of big,only this for front small sticks can conbine with rear small stick of small  to  a Min
				return(0);
			}
		}
	}
	return(0);
}
/*for test*/
int test()
{
	return(0);
}
/*main process*/
int MainProc()
{
	while(scanf("%d",&n)!=EOF&&n>0)
	{
		//init
		int i=0;
		Sump=0;
		for(i=0;i<=n-1;i++)
		{
			scanf("%d",&parts[i]);
			Sump+=parts[i];
		}
		sort(parts,parts+n,cmp);
		for(Min=parts[0];Min<=Sump;Min++)//prune
		{
			memset(visp,false,sizeof(visp));
			flag=0;
			removenum=Min;
			numN=Sump/Min;
			if(Sump%Min==0)//prune
			{
				DFS(0,0,0);
			}
			if(flag)
			{
				break;
			}
		}
		printf("%d\n",Min );
	}
	return(0);
}
int main(int argc, char const *argv[])
{
	/* code */
	//test();
	MainProc();
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值