#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】解题报告+思路+代码
最新推荐文章于 2019-07-28 15:30:18 发布