【POJ1011 Sticks】解题报告+思路+代码

12 篇文章 0 订阅
5 篇文章 0 订阅
#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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值