#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <iostream>
//#define INPUT
using 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;
}
【POJ1011 Sticks】解題報告+思路+代碼
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.