ACWing167木棒

题目传送门:https://www.acwing.com/problem/content/description/169/
题目大意:
乔治拿来一组等长的木棒,将它们随机地砍断,使得每一节木棍的长度都不超过50个长度单位。然后他又想把这些木棍恢复到为裁截前的状态,但忘记了初始时有多少木棒以及木棒的初始长度。请你设计一个程序,帮助乔治计算木棒的可能最小长度。每一节木棍的长度都用大于零的整数表示。注意: 数据中可能包含长度大于50的木棒,请在处理时忽略这些木棒。
输入格式
输入包含多组数据,每组数据包括两行。
第一行是一个不超过64的整数,表示砍断之后共有多少节木棍。
第二行是截断以后,所得到的各节木棍的长度。
在最后一组数据之后,是一个零。
输出格式
为每组数据,分别输出原始木棒的可能最小长度,每组数据占一行。
输入样例:
9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0
输出样例:
6
5
不分析了,此题乃剪枝练习好题,李煜东大神的书《算法竞赛进阶指南》上写得非常棒,关于该题的分析以后补上吧。直接上代码:

#include<bits/stdc++.h>
using namespace std;
int a[66];
bool vis[66];
int len,sum,val,n,cnt;
void init(){
	memset(a,0,sizeof(a));
	sum = 0;
	len = 0;
	val = 0;
}
void read(){
	int t = 0,x=0;
	cin >> n;
	if(n == 0) return;
	for(int i = 1;i<= n;i++){
		scanf("%d",&x);
		if(x > 50) continue;
		a[++t] = x;
		sum = sum + a[t];
		val = max(val,a[t]);
	}	
	n = t;
}
bool dfs(int sticks,int cab,int last){
	//sticks:表示当前处理第几根新棒子了。
	//cab:表示当前这个棒子已经拼接到多长了。
	//last:表示用到了第几根砍断的棒子了 
	if(sticks > cnt) return true;//当拼接完cnt根新的等长棒子就说明成功了。
	if(cab == len) return dfs(sticks + 1,0,1);//当当前这根棒子已拼至len长度,则开始处理新的一根待拼接的棒子
	int fail = 0;
	for(int i = last; i<= n; i++){
		if(!vis[i] && cab +a[i] <= len && fail != a[i]){
			vis[i] = true;
			if(dfs(sticks,cab + a[i],i+1)) return true;
			vis[i] = false;
			fail = a[i];//记录当前失败的小木棍长度,后面碰到相等的小木棍要拼接时肯定失败,供及时剪掉用 
			if(cab == 0 || cab + a[i] == len){ //对等效情况及时剪枝。此剪枝不容易想到。 
				return false;
			} 
		}
	}
	return false;//当所有分支均尝试但都失败。 
} 
void solve(){
	sort(a+1,a+1+n);
	reverse(a+1,a+1+n);
	for(len = val ; len <= sum; len ++){
		if(sum % len != 0 ) continue;
		cnt = sum / len;
		memset(vis,0,sizeof(vis));
		if(dfs(1,0,1)) break;   //从小到大枚举的长度,那么当只要能拼成长度一致为len的必为此题的解。 
	}
	cout << len<< endl;
}
int main(){
	
	while(1){
		init();
		read();
		if(n == 0)break;
		solve();		
	}	
	return 0;
}
发布了88 篇原创文章 · 获赞 22 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/xuechen_gemgirl/article/details/89642817
167