[置顶] POJ 1011 最小的木棒 (dfs+剪枝|| 搜索好题)

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

Source

Central Europe 1995

Translator

北京大学程序设计实习, Xie Di


题解:

InitLen为所求的最短原始棒长,maxlen为给定的棒子堆中最长的棒子,sumlen为这堆棒子的长度之和,那么InitLen必定在范围[maxlensumlen]

 

根据棒子的灵活度(棒子越长,灵活度越低) DFS前先对所有棒子降序排序

 

剪枝:

1、  由于所有原始棒子等长,那么必有sumlen%Initlen==0

2、  若能在[maxlen,sumlen-InitLen]找到最短的InitLen,该InitLen必也是[maxlen,sumlen]的最短;若不能在[maxlen,sumlen-InitLen]找到最短的InitLen,则必有InitLen=sumlen

3、  由于所有棒子已降序排序,在DFS时,若某根棒子不合适,则跳过其后面所有与它等长的棒子;

4、  最重要的剪枝:对于某个目标InitLen,在每次构建新的长度为InitLen的原始棒时,检查新棒的第一根stick[i],若在搜索完所有stick[]后都无法组合,则说明stick[i]无法在当前组合方式下组合,不用往下搜索(往下搜索会令stick[i]被舍弃),直接返回上一层




附上代码:


#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 111;
int a[N],vis[N];
int num,tem,n;           // num:原木块的数量, tem: 原木块的最小长度;      n:切断后木块的数量 

bool dfs(int cnt,int len,int index)  //每次从第index木头拼凑,每块木头都得用上。枚举后就不会再用到它了 
{
	if(cnt==num) return true;         // 如果能拼凑到num块木头 
	for(int i=index;i<=n;i++) {
		if(vis[i]) continue;
		vis[i]=1;
		if(len+a[i]==tem) {
			if(dfs(cnt+1,0,index+1)) return true;       // 能够拼凑成一块木头...木头数量+1,len更新为0 
			else {
				vis[i]=0;
				return false;
			}
		}
		else if(len+a[i]<tem) {
			if(dfs(cnt,len+a[i],index)) return true;
			else vis[i]=0;
			if(len==0) return false; // 第一块木头不能别拼凑,以后的木块都不用再枚举了... 
			while(a[i]==a[i+1]) i++;  //根节点后面相同的节点(木头)不用再枚举了 
		}
		else vis[i]=0;
	}
	return false;
}

int main()
{
	int i,j,Max,sum;
	while(scanf("%d",&n)==1&&n) {
		Max=sum=0;
		for(i=1;i<=n;i++) {
			scanf("%d",&a[i]);
			Max=max(Max,a[i]);
			sum+=a[i];
		}
		sort(a+1,a+1+n,greater<int>());     //木块从大到小排序,因为小木块比大木块具有灵活性.。。。 
		for(tem=Max;tem<=sum;tem++) {       //ans从最长的木块枚举到总长 
			if(sum%tem) continue;           //木块总长是ans的倍数 
			num=sum/tem;
			memset(vis,0,sizeof(vis));
			if(dfs(0,0,1)) break;
		}
		printf("%d\n",tem);
	}
	return 0;
}




// 这题能真正搞懂。才会是dfs入门,理解回溯,树的深度 ... 递归时的种种问题。。。。。 
//好题,mark 







你可能感兴趣的:([置顶] POJ 1011 最小的木棒 (dfs+剪枝|| 搜索好题))