1. 程式人生 > >P1120 小木棍 [資料加強版]

P1120 小木棍 [資料加強版]

題目描述:

喬治有一些同樣長的小木棍,他把這些木棍隨意砍成幾段,直到每段的長都不超過 50 。現在,他想把小木棍拼接成原來的樣子,但是卻忘記了自己開始時有多少根木棍和它們的長度。給出每段小木棍的長度,程式設計幫他找出原始木棍的最小可能長度。

想法:

首先在讀入的時候忽略掉長度大於50的木棍(最重要),順便還要記錄一下最短的木棍和最長的木棍的長度(為之後的運算剪枝),然後按照木棍的長度做一次排序(因為木棍長度都小於等於50,本人用的是桶排序),接著就可以開始搜尋了。

搜尋時的策略:

1、從最長的木棍的長度開始,列舉原先每根木棍的可能長度,一直到(剪枝一:) 總長度的一半(如果原先木棍的數量不為1,則原先木棍的長度必然小於所有木棍總長度的一半)

。(小貼士:如果列舉完後,仍然沒有輸出答案,則直接輸出總長度即可)(因為如果一直到總長度的一半都沒有解,則說明原先的木棍就只有一根而已)

2、從(剪枝二)最長的木棍開始選擇(如從最短的木棍開始搜尋,會增加回溯次數,具體原因留給讀者自行思考),逐一遞減,如果當前已選擇的木棍的總長度加上目前準備選擇的木棍的長度小於等於需要的長度,則分別搜尋是否選擇選擇該木棍的情況(類似於01揹包)

3、如果已經拼湊好了所有的木棍,則直接輸出當前列舉的原先木棍長度並結束程式(如果已找到最優解,則不需要繼續搜尋);如果當前拼湊的木棍的長度恰好等於列舉的原先木棍的長度,那麼重新從最長的木棍開始搜尋(注意:一定是從最長的木棍開始搜尋,但要判斷它是否已被選定,在每次選擇的時候用一個 bool 函式標記即可)。

4、具體情況見程式碼:

#include <bits/stdc++.h>
using namespace std;

int num[500010],maxn,minn,sum;

int max(int x,int y){
	return x>y?x:y;
}

int min(int x,int y){
	return x>y?y:x;
}

void dfs(int wait,int already,int need,int can){    //從前往後依次為 還需要拼湊得到的木棍數量 
	int i;			//			    	當前已經拼湊得到的木棍的長度 
	if(wait==0){	//如果已經完成所有的木棍的拼湊,則直接輸出結果    需要得到的木棍的長度 
		printf("%d",need);			//	當前可以使用的最長木棍的長度 
		exit(0);
	}
	if(already==need){//如果當前拼湊出的木棍的長度等於需要得到的木棍的長度,則開始拼湊下一根 
		dfs(wait-1,0,need,maxn);
		return ;
	}
	for(i=can;i>=minn;i--)
		if(num[i] && i+already<=need){
			num[i]--;
			dfs(wait,already+i,need,i);
			num[i]++;
			if(already==0 || already+i==need)
				return ;
		}
}
	
int main(){
//	freopen("in.txt","r",stdin);
	int i,j,k,m,n,temp;
	scanf("%d",&n);
	for(i=1;i<=n;i++){
		scanf("%d",&k);
		if(k<=50){		//忽略長度小於等於50的木棍 
			sum+=k;
			num[k]++;
			minn=min(k,minn);
			maxn=max(k,maxn);
		}
	}
	temp=sum/2;
	for(i=maxn;i<=temp;i++)			//列舉每一種可能選定的長度 
		if(sum%i==0)
			dfs(sum/i,0,i,maxn);
	printf("%d",sum);
	return 0;
}