木棍加工 [搜索]

题目大意

有很多根等长木棍,现将他们随机砍断,已知砍断后的每一节小木棍的长度,求原木棍可能的最小长度。

思路

易知原木棍的长度一定大于砍断的最大木棍长度,数据小,可以枚举原始木棍的长度len:最大小木棍长度val~小木棍长度之和sum
显然len能整除sum,原木棍的数量cnt=sum/len.
对于枚举的每个长度进行搜索,状态设计为stk(已经拼好的原始木棍数量),snow(正在拼的原始木棍长度),last(当前要使用的小木棍);
递归边界:stk==cnt+1,即所有原始木棍已经拼好.
考虑剪枝:
1.木棍顺序等效性:从大到小取小木棍;
2.空木棍的等效性:对于一根新的空木棍,如果拼入的第一根木棍就萎了,那就直接回溯;
3.等长木棍等效性:对于当前原始木棍,如果某个木棍的长度拼进去是萎的,那么相同长度的木棍也不要拼进去了;
4.贪心:对于当前原始木棍,拼入一根木棍后如果刚好拼满,而且在之后的过程中萎了,那么也可以直接回溯。(“再用一根木根恰好拼完”比“再用几根拼完”更优)。
想清楚了就身心愉快 想不清楚就多想几遍

Code

#include <cstring>
#include <algorithm>
#include <cstdio>
#define ll long long
using namespace std;
inline int read(){
	int x=0,f=1;char c=getchar();
	while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
	while (c>='0'&&c<='9') {x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return x*f;
}
const int maxn(100);
int a[maxn],v[maxn],n,m,len,cnt,sum,val;
bool dfs(int stk,int snow,int last){
	if (stk==cnt+1) return 1;
	if (snow==len) return dfs(stk+1,0,1);
	int fail=0;
	for (int i(last);i<=m;++i)
		if (!v[i]&&snow+a[i]<=len&&fail!=a[i]){
			v[i]=1;
			if (dfs(stk,snow+a[i],i+1)) return 1;
			fail=a[i];
			v[i]=0;
			if (snow==0||snow+a[i]==len) return 0;
		}
	return 0;
}

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

void init(){
	sum=0,val=0,m=0;
	for (int i(1);i<=n;++i){
		if (a[i]>50) continue;//它这个题目数据是萎的,可能大于50,是出题人的锅
		a[++m]=read(),sum+=a[m],val=max(val,a[m]);
	}
        sort(a+1,a+m+1,cmp);
}

void doit(){
	for (len=val;len<=sum;++len){
		if (sum%len) continue;
		cnt=sum/len;
		memset(v,0,sizeof v);
		if (dfs(1,0,1)) {printf("%d
",len);break;}
	}
}

signed main(){
	n=read();
	while (n!=0){
		init();
		doit();
		n=read();
	}
	return 0; 
}

原文地址:https://www.cnblogs.com/cancers/p/12018978.html