【POJ1011 Sticks】解題報告+思路+代碼

#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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章