#include <cstring>#include <cstdio>#include <cstdlib>#include <algorithm>#include <iostream>//#define INPUTusing namespace std;/** Problem : poj1011 - Sticks Begin Time : 13:00 p.m. 15th/mar/2012 End Time : 15:10 p.m. 15th/mar/2012 【看別人的報告過的】 Reference : 程式設計線上導引 , PKU出版。 Knowledge : DFS,回溯法,重疊子問題用遞迴解,剪枝思想 Thought: 我們用遞迴的思想解決這個問題,設某一個函數 f(totalSticks,leftNum,leftLenth,totalLenth), 其中 totalSticks代表總共有多少根棍子。 leftNum代表剩了多少根棍子。 leftLenth代表拼的當前棍子還有多長才能拼成totalLenth。 totalLenth代表要拼出來的長度。 bool f(totalSticks,leftNum,leftLenth,totalLenth) { int i; if( leftLenth == 0 && leftNum == 0 ) return true; if( leftLenth == 0 ) leftLenth = totalLenth; //拼完一個拼下一個 for( i = 0 ; i < totalSticks ; i++) { if(used[i]) continue; ///這根棍子被用過 if(sticks[i] > leftLenth) continue; ///當前選擇的棍子太長了 if(totalSticks(totalSticks,leftNum-1,leftLenth-sticks[i],totalLenth) return true; used[i] = false;回溯 ////下方這個剪枝十分重要! IF( leftlenth == nums[i] || leftlenth == totallenth ) BREAK; //// 解釋如下: ///// ① leftlenth = nums[i] : nums[i]是所拼長度的最後一個棍子 ///// 這種情況下,把nums[i]拼出來一個目標長度之後還能運行到剪枝語句 ///// 就證明拼完了nums[i]剩下的棍子拼不出來目標長度 ///// 如果不break,繼續執行 ///// 設nums[i]可以被其他的棍子替代,並且nums[i]可以在其他情況拼出來目標長度 ///// 那麼把這兩種情況互換一下,即nums[i]拼上,然後用這些替代nums[i]的小木棍去拼其他情況 ///// 這樣,nums[i]拼完之後就可以拼出來目標長度了 ///// 而這跟既成事實是矛盾的,所以要break; ///// ② leftlenth = totallenth,這就證明nums[i]是第一個棍子 ///// 第一個都拼不出來,那麼nums[i]就註定無論在任何情況下都拼不出目標長度,所以break; } return false; } 我們將所有stick的長度讀入數組nums中,並按照長度降序排列。 for( len = nums[0]; len <= sum; len++) { if(f(totalSticks,totalSticks,len,len)) printf("%d\n",len); } len的取值範圍是nums最大值到nums的和。 教訓: 對於重疊子問題要使用遞迴結構,而這道題,重疊子問題我都想到了 但是沒想到使用遞迴結構,這道題的收穫就是 “拼出目標長度”的遞迴寫法 自己錯的地方也在《導引》中提到了,是第一個錯誤點。 這道題還是很牛逼的,要多看看*/const int c0de4fun = 100;int nums[c0de4fun];int used[c0de4fun];int comp(const void *a,const void *b){ return (*(int*)b - *(int*)a);}bool solve(int tot_num,int left_num,int left_len,int tot_len){ int i; if ( left_num == 0 && left_len == 0 ) return true; if ( left_len == 0 ) left_len = tot_len; for( i = 0 ; i < tot_num ; i++ ) { if( used[i] ) continue; if( nums[i] > left_len ) continue; used[i] = true; if( solve(tot_num,left_num-1,left_len-nums[i],tot_len) ) return true; used[i] = false; if( left_len == tot_len || nums[i] == left_len ) break; } return false;}int main(){#ifdef INPUT freopen("b:\\acm\\poj1011\\input.txt","r",stdin);#endif int n,i,len,sum; while ( scanf("%d",&n) != EOF ) { if( n == 0 ) break; memset(nums,0,sizeof(nums)); memset(used,0,sizeof(used)); sum = len = 0; for (i = 0 ; i < n ; i++) { scanf("%d",&nums[i]); sum += nums[i]; } qsort(nums,i,sizeof(int),comp); for(len = nums[0]; len <= sum ; len++) { if ( sum % len != 0) continue; if(solve(i,i,len,len)) printf("%d\n",len); } } return 0;}