1. 程式人生 > 其它 >題解0007:小木棍(P1120)

題解0007:小木棍(P1120)

 

 (錯誤記錄)

題目連結:https://www.luogu.com.cn/problem/P1120

題目描述:幾根同樣長的木棍,小冥把它們隨意砍成了n段;

     然後他又吃飽了撐的想把木棍拼上;

     但是這個小冥是小年痴呆,他忘了他原來是咋切的了;

     現在要寫一段程式,輸入n和切斷的每段木棍的長,輸出原始木棍的最小可能長度。

題目思路:搜尋剪枝,這道題搜尋其實很好說,直接從小到大列舉長度,然後遞迴判斷可行性;

     難點還是在剪枝上,如果不用剪枝的話只能得9分左右,這道題要用到7個剪枝;

     具體的詳見程式碼;

程式碼:

#include<bits/stdc++.h>
using namespace std;
int n,arr[66]={0},ans,brr[66]={0},we,wer;//arr存木棍長度,brr存木棍是否被用過 
bool cmp(int a,int b){//將所有木棍從大到小排序 
	return a>b; 
}
bool dfs(int a,int b,int c){//a是假定小木棍的剩餘長度,b是迴圈初始值,c是木棍根數 
	if(c==wer){//木棍根數對上了 
		return 1;
	}
	if(a==0){//一根木棍拼完 
		if(dfs(we,1,c+1)){//根數+1 
			return 1;
		}
	}
	for(int i=b;i<=n;++i){
	//剪枝3:下次迴圈的初始值完全可以接上上次迴圈的結束值 
		if(arr[i]<=a&&brr[i]!=1){//木棍沒被用過&&長度沒超 
			brr[i]=1;//標記 
			if(dfs(a-arr[i],i+1,c)){//-去長度 
				return 1;
			}
			brr[i]=0;//回溯 
			if(a==arr[i]||a==we){
			//剪枝4:如果這個木棍長度與剩餘長度一樣,這已經是最優解了,還不行,那直接返
			//剪枝5:如果剩餘長度與假定木棍長度一樣,和剪枝4一樣,直接返 
				return 0;//如果寫break可能過不了 
			}
			while(arr[i+1]==arr[i]){
			//剪枝6:因為排了序,所以有很多相等的數在一起,遇到相等的數直接跳 
				i++;
			}
		}
	}
	return 0;
}
int main(){
	cin>>n;
	for(int i=1;i<=n;++i){
		cin>>arr[i];
		ans+=arr[i]; 
	}
	sort(arr+1,arr+n+1,cmp);
	for(int i=arr[1];i<=ans/2;++i){//這個數從最小木棍長度開始列舉 
	//剪枝1:如果列舉到所有木棍長度+起來/2還沒有找到,那結果就一定是所有數加起來了,往下搜沒意義了 
		if(ans%i==0){
		//剪枝2:只有能被總長度整除的數才有可能是結果,不滿足條件的直接扔掉 
			we=i;
			wer=ans/we;
			if(dfs(we,1,0)){//遞迴判斷可行性 
				cout<<we;
				return 0;//可以直接結束 
			}
		}
	}
	cout<<ans;
	return 0;
}