1. 程式人生 > >poj(1011)——Sticks(經典的dfs+剪枝)

poj(1011)——Sticks(經典的dfs+剪枝)

題目的大致意思是:

現在有n根木棍,然後需要把它們拼成同樣長度的木棍,問滿足這個條件的最短的長度是多少?

想法嘛:那肯定是dfs把長度搜一遍就好,但問題的關鍵是這裡會超時。那麼就要用到剪枝的原理了。

以下部分是來自於pku的gw老師說噠大笑

1)不要在同一個位置多次嘗試相同長度的木棒(在某一次拼接時選擇長度為s的木棒導致拼接失敗,則在同一位置嘗試下一根木棒時,要跳過所有長度為s的木棒)

2)如果由於以後的拼接失敗,需要重新調整第i根棍子的拼法,則不會考慮替換第i根棍子中的第一根木棒。

3)不要希望通過僅僅替換已經拼好的棍子的最後一根木棒就能改變失敗的局面。

4)拼每一根棍子的時候,應確保已經拼好的部分,長度是從長到短排列的(因為我們應該先拼長的,長的可能性小)

排除方法:每次找一根木棒的時候,只要這不是一根棍子的第一條木棒,那麼不應該從下標為0的木棒開始找,而應該從剛剛接上去的那條木棒的下一條開始找(當然,我們要先對木棒進行從大到小的排序)

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
using namespace std;
#define maxn 70
int a[maxn],n,L,vis[maxn],lastnum=0;
bool cmp(int a,int b){
	return a>b;
}
//當前還餘下的棍子的個數和還缺的長度 
bool dfs(int m,int l){
	if(m==0&&l==0) return true;
	if(l==0)  l=L;
	int s=1;
	//剪枝4 
	if(l!=L){
		s=lastnum+1;
	}
	for(int i=s;i<=n;i++){
		if(!vis[i-1]&&i>1&&a[i]==a[i-1]) continue;	//剪枝1 
		if(!vis[i]&&a[i]<=l){
			vis[i]=1;
			lastnum=i;
			if(dfs(m-1,l-a[i])) return true;
			else{
				vis[i]=0;
				if(L==l||a[i]==l) return false;		//剪枝2,3 
			}
		}
	}
	return false;
}
int main(){
	while(~scanf("%d",&n)){
		if(n==0) break;
		memset(a,0,sizeof(a));
		int sum=0,lmax=-1;
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);
			if(a[i]>lmax) lmax=a[i];
			sum+=a[i];
		}
		int i=0;
		sort(a+1,a+1+n,cmp);
		#if 1
		for(i=lmax;i<=sum/2;i++){
			if(sum%i) continue;
			L=i;
			memset(vis,0,sizeof(vis));
			lastnum=0;
			if(dfs(n,i)){
				printf("%d\n",i);
				break;
			}
		}
		if(i>sum/2) printf("%d\n",sum);
		#endif
	}
}
/*
9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0
*/