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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章