地址:http://poj.org/problem?id=1011
問題描述:
思路1:
題意: 給你n 木棍碎片,要求還原原木棍,且原木棍等長,問最短長度是多少。
分析: 各種剪枝
1 /*1:越長的木棍對后面木棍的約束力越大,因此要把小木棍排序, 2 按木棍長度從大到小搜索,這樣就能在盡可能靠近根的地方 3 剪枝。 4 :當出現加上某根木棍恰好能填滿一根原始木棍,但由在后面的 5 搜索中失敗了,就不必考慮其他木棍了,直接退出當前的枚舉。 6 :考慮每根原始木棍的第一根木棍,如果當前枚舉的木棍長度無 7 法得出合法解,就不必考慮下一根木棍了,當前木棍一定是作 8 為某根原始木棍的第一根木棍的,現在不行,以后也不可能得 9 出合法解。也就是說每根原始木棍的第一根小木棍一定要成 10 功,否則就返回。 11 :剩下一個通用的剪枝就是跳過重復長度的木棍,當前木棍跟 12 它后面木棍的無法得出合法解,后面跟它一樣長度的木棍也 13 不可能得到合法解,因為后面相同長度木棍能做到的,前面這 14 根木棍也能做到。 15 */ 16 #include<stdio.h> 17 #include<string.h> 18 #include<stdlib.h> 19 int cmp(const void*p1,const void*p2) 20 { 21 return *(int*)p2-*(int*)p1; 22 } 23 int a[100],v[100]; 24 int n,len,s,tot,min; 25 bool dfs(int k,int mi,int left) 26 { 27 int i; 28 if(left==min) 29 return 1; 30 for(i=k;i<=n;i++) 31 if(!v[i]&&a[i]<=mi) 32 { 33 v[i]=1; 34 if(a[i]==mi&&dfs(1,len,left-a[i])) 35 return 1; 36 else if(dfs(i+1,mi-a[i],left-a[i])) 37 return 1; 38 v[i]=0; 39 if(a[i]==min)return 0; 40 if(left==tot)return 0; 41 if(mi==len) return 0; 42 while(a[i+1]==a[i]) 43 i++; 44 } 45 return 0; 46 } 47 int main() 48 { 49 int i,res; 50 while(scanf("%d",&n),n) 51 { 52 tot=0; 53 for(i=1;i<=n;i++){ 54 scanf("%d",&a[i]); 55 tot+=a[i]; 56 } 57 qsort(a+1,n,sizeof(a[0]),cmp); 58 len=a[1]; 59 res=len; 60 memset(v,0,sizeof(v)); 61 res=tot; 62 for(;len<tot;len++) 63 if(tot%len==0&&dfs(1,len,tot)) 64 { 65 res=len; 66 break; 67 } 68 printf("%d\n",res); 69 } 70 return 0; 71 }
思路2:
思想很簡單,一個接一個的把木棍拼起來,最后把木棍用光。
* 關鍵的地方是幾個剪枝技巧:
* 設所有木棍的總長度為 Sum, 最終的答案(長度)是 L。
* 1. 首先要明白, Sum一定要能被 L 整除。
* 2. L 一定 大於等於 題目給出的最長的木棍的長度 Max。
* 由上述兩點,我們想到,可以從 Max 開始遞增地枚舉 L,
* 直到成功地拼出 Sum/L 支長度為 L 的木棍。
* 搜索種的剪枝技巧:
* 3. 將輸入的輸入從大到小排序,這么做是因為一支長度為 K
* 的完整木棍,總比幾支短的小木棍拼成的要好。
* 形象一些:
* 如果我要拼 2 支長為8的木棍,第一支木棍我拼成
* 5 + 3
* 然后拼第二支木棍但是失敗了,而我手中還有長為 2 和 1
* 的木棍,我可以用 5 + 2 + 1 拼好第一支,再嘗試拼第二
* 支,仔細想一想,就會發現這樣做沒意義,注定要失敗的。
* 我們應該留下 2+1 因為 2+1 比 3 更靈活。
* 4. 相同長度的木棍不要搜索多次, 比如:
* 我手中有一些木棍, 其中有 2 根長為 4 的木棍, 當前搜索
* 狀態是 5+4+.... (即表示長度為 5,4,2 的三支拼在一起,
* ...表示深層的即將搜索的部分), 進行深搜后不成功,故我
* 沒必要用另一個 4 在進行 5+4+...
* 5. 將開始搜索一支長為 L 的木棍時,我們總是以當前最長的未
* 被使用的 木棍開始,如果搜索不成功,那么以比它短的開始
* 那么也一定不能取得全局的成功。因為每一支題目給出的木棍
* 都要被用到。
* 如果,有
* 4
* 5 4 4 3 2
* 想拼成長為 6 的木棍,那么從 5 開始, 但是顯然沒有能與 5
* 一起拼成 6 的,那么我就沒必要去嘗試從 4 開始的,因為
* 最終 5 一定會被遺棄。在拼第 2 3 ... 支木棍時,一樣。
* 6. 最后的最簡單的一個就是,
* for(int i = 0; i < n; i++)
* for(int j = 0; j < n; j++)
* {}
* 與
* for(int i = 0; i < n; i++)
* for(int j = i+1; j < n; j++)
* {}
* 的區別,這個不多說了。
* 7. 我用過的另一個剪枝,但是對 poj 的數據效果一般,
* 用一個數組, Sum[i] 保存 第 i 個木棍之后,即比第 i 枝
* 木棍短或與之相等所有的木棍的長度之和。
* 試想,如果剩余的所有木棍加在一起都不能和我當前的狀態拼
* 出一直長為 L 的木棍(從長度來看),還有必要搜下去么?
代碼如下:
1 #include <iostream> 2 #include <string.h> 3 #include <stdio.h> 4 #include <algorithm> 5 6 using namespace std; 7 int sticks[65],n,sum,num,l;//l最小的與木棒的長度 num:圓木棒的個數 sum:圓木棒的總長度 8 bool mark[65]; 9 bool cmp(int a,int b) 10 { 11 return a>b; 12 } 13 14 //s:已經組成的小木棒的個數,le:當前搜索時正在組成的小木條的長度。pos:要組合的小木條的下標位置 15 bool dfs(int s,int le,int pos) 16 { 17 int i; 18 bool sign = (le == 0?true:false); 19 if(s==num)return true; 20 for(i = pos + 1;i < n;i++) 21 { 22 if(mark[i])continue;//如果這個小木棒組合過則從下一個開始 23 if(le + sticks[i]==l)//如果組合剛好組成l長度的木棒那么就要組合下一根木棒了即第s+1根 24 { 25 mark[i] = true; 26 if(dfs(s+1,0,-1))//第s根已經組合成功了組合下一根 27 return true; 28 mark[i] = false; 29 return false;//如果組合失敗了那么就要返回false 而且這個木棒的狀態應還原為沒組合過 30 } 31 else if(le + sticks[i]<l) 32 //如果組合當前這根后長度仍然小於l那么從i開始往下找繼續組合第S根木棒 33 { 34 mark[i] = true; 35 if(dfs(s,le+sticks[i],i))//如果可以組成就返回true 36 return true; 37 mark[i] = false; 38 if(sign)return false; 39 while(sticks[i]==sticks[i+1])i++; 40 } 41 } 42 return false; 43 } 44 45 int main() 46 { 47 while(scanf("%d",&n)!=EOF,n) 48 { 49 sum = 0; 50 51 for(int i = 0; i < n; i++) 52 { 53 scanf("%d",&sticks[i]); 54 sum += sticks[i]; 55 } 56 sort(sticks,sticks+n,cmp);//將木棒從大到小排序 57 for(l = sticks[0]; l <= sum; l++)//從最大的開始搜索 58 { 59 if(sum%l==0)//如果該長度可以被整除那么可能是結果,否則繼續 60 { 61 num = sum/l;//num:記錄該情況下可以組成的木棒的數目。 62 memset(mark,false,sizeof(mark));//每種情況都要進行初始化,把所有的木棒的使用狀態設為false 63 if(dfs(1,0,-1))//當返回true時說明搜索成功了,可以組成該長度木條 64 { 65 printf("%d\n",l); 66 break; 67 } 68 } 69 } 70 } 71 return 0; 72 }
思路3:
1 #include <iostream.h> 2 #include <memory.h> 3 #include <stdlib.h> 4 int T, S; 5 int L; 6 int anLength[65]; 7 int anUsed[65]; 8 int i,j,k; 9 int Dfs(int nUnusedSticks, int nLeft); 10 int MyCompare( const void * e1, const void * e2) { 11 int * p1, * p2; 12 p1 = (int * ) e1; 13 p2 = (int * ) e2; 14 return * p2 - * p1; 15 } 16 main() 17 { 18 while(1) { 19 cin >> S; 20 if( S == 0 ) 21 break; 22 int nTotalLen = 0; 23 for( int i = 0; i < S; i ++ ) { 24 cin >> anLength[i]; 25 nTotalLen += anLength[i]; 26 } 27 qsort(anLength,S,sizeof(int),MyCompare); 28 for( L = anLength[0]; L <= nTotalLen / 2; L ++ ) { 29 if( nTotalLen % L) 30 continue; 31 memset( anUsed, 0,sizeof(anUsed)); 32 if( Dfs( S,L)) { 33 cout << L << endl; 34 break; 35 } 36 } 37 if( L > nTotalLen / 2 ) 38 cout << nTotalLen << endl; 39 } // while 40 } 41 int Dfs( int nUnusedSticks, int nLeft) 42 // nLeft表示當前正在拼的棍子和 L 比還缺的長度 43 { 44 if( nUnusedSticks == 0 && nLeft == 0 ) 45 return true; 46 if( nLeft == 0 ) //一根剛剛拼完 47 nLeft = L; //開始拼新的一根 48 for( int i = 0;i < S;i ++) { 49 if( !anUsed[i] && anLength[i] <= nLeft) { 50 if( i > 0 ) { 51 if( anUsed[i-1] == false 52 && anLength[i] == anLength[i-1]) 53 continue; //剪枝3 54 } 55 anUsed[i] = 1; 56 if ( Dfs( nUnusedSticks - 1, 57 nLeft - anLength[i])) 58 return true; 59 else { 60 anUsed[i] = 0;//說明不能用i作為 61 //第1條, 62 //那么要拆以前的 63 //棍子,i還可能用 64 //在以前的棍子里, 65 //因此要 anUsed[i] = 0; 66 if( anLength[i] == nLeft || nLeft == L) 67 return false;//剪枝2、1 68 } 69 } 70 } 71 return false; 72 }
思路4:
1 #include <stdio.h> 2 #include <stdlib.h> 3 int sticks[100],n; 4 bool used[100]; 5 int cmp(const void *x,const void *y) 6 { 7 return *(int *)y - *(int *)x; 8 } 9 bool find(int left,int num,int len) 10 { 11 int i; 12 if(left == 0 && num == 0) 13 return 1; 14 if(left == 0) 15 left = len; 16 for(i = 0;i < n;i ++) 17 { 18 if(sticks[i] <= left && !used[i]) 19 { 20 used[i] = 1; 21 if(find(left - sticks[i],num-1,len)) 22 return 1; 23 used[i] = 0; 24 if(sticks[i] == left || left == len) 25 return 0; 26 } 27 } 28 return 0; 29 } 30 31 int main() 32 { 33 int i,sum = 0; 34 while(scanf("%d",&n) != EOF && n) 35 { 36 sum = 0; 37 for(i = 0;i < n;i ++) 38 { 39 scanf("%d",&sticks[i]); 40 sum += sticks[i]; 41 used[i] = 0; 42 } 43 qsort(sticks,n,sizeof(int),cmp); 44 for(i = sticks[0];i <= sum;i ++) 45 { 46 if((sum % i == 0) && find(i,n,i)) 47 { 48 printf("%d\n",i); 49 break; 50 } 51 } 52 } 53 return 0; 54 }
