poj1011 | NKOJ 1423 木棍

P1423【分类练习6.搜 索】小木棍
时间限制 : 10000 MS   空间限制 : 65536 KB
问题描述

乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50。

现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。

给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。

输入格式

输入文件共有二行。

第一行为一个单独的整数N表示看过以后的小木柜的总数,其中N≤60,

第二行为N个用空个隔开的正整数,表示N跟小木棍的长度。

输出格式

输出文件仅一行,表示要求的原始木棍的最小可能长度。

样例输入


5 2 1 5 2 1 5 2 1

样例输出: 6

分析:
首先答案肯定是所有木棍长度之和的因数,并且比最长的木棍长。
从小到大枚举每一个这样的数,用DFS来验证。
DFS思路:凑成一个一个长木条,凑足数量为止。
DFS(x, used, cur) 表示当前试用x号木棍,当前的这根大木条已经用了used米,凑好的大木条的总长。
剪枝:
(1)每凑完一根大木条,新的大木条用上剩下的木棍中最长的那一根,这样对后面约束能力更强。
(2)(强) 如果有一样的木棍,只讨论一次。
貌似这两条已经能a了。
为了方便处理,将所有木棍从大到小排序。
代码如下:
#include
#include
#include
#include
#include
#include
#include
#include
#define LL long long
#define CLEAR(xxx) memset(xxx,0,sizeof(xxx))
using namespace std;
const int maxn=70,inf=1e9;

int n,len,s[maxn],tot,_min,_max;
vector divs;
bool vis[maxn];

void depart(int p){   //找出要枚举的因数 
	divs.clear();
	int m=(int)sqrt(p);
	for(int i=1;i<=m;i++)
		if(p%i==0){
			if(i>=_max)divs.push_back(i);
			if(i*i!=p&&p/i>=_max) divs.push_back(p/i);
		}
	sort(divs.begin(),divs.end());
}

bool dfs(int x,int used,int cur){
	int i;
	if(len==used){
		cur+=len;
		if(cur==tot) return true;
		x=1;
		while(vis[x])x++;  //因为是排过序的,找出剩的最长的木棍 
		vis[x]=true;
		if(dfs(x,s[x],cur)) return true;
		vis[x]=false;
	}
	else {
		for(i=x;i<=n;i++){
			if(i>1&&s[i]==s[i-1]&&!vis[i-1]) continue; //避免重复搜索相同长度 
			if(!vis[i]&&used+s[i]<=len){
				vis[i]=true;
				if(dfs(i,used+s[i],cur)) return true;
				vis[i]=false;
			}
		}
	}
	return false;		
}

bool cmp(int x,int y){
	return x>y;
}

int main(){
	int i,j;
    while(cin>>n&&n){
    	tot=_max=0; _min=inf;
    	for(i=1;i<=n;i++){
			cin>>s[i];
			tot+=s[i];
			_max=max(_max,s[i]);
		}
		sort(s+1,s+1+n,cmp);
		depart(tot);
		for(i=0;i


你可能感兴趣的:(搜索)