POJ1011 木棒(dfs+剪枝)

问题重述:

Description
乔治拿来一组等长的木棒,将它们随机地砍断,使得每一节木棍的长度都不超过50个长度单位。然后他又想把这些木棍恢复到为裁截前的状态,但忘记了初始时有多少木棒以及木棒的初始长度。请你设计一个程序,帮助乔治计算木棒的可能最小长度。每一节木棍的长度都用大于零的整数表示。

Input
输入包含多组数据,每组数据包括两行。第一行是一个不超过64的整数,表示砍断之后共有多少节木棍。第二行是截断以后,所得到的各节木棍的长度。在最后一组数据之后,是一个零。

Output
为每组数据,分别输出原始木棒的可能最小长度,每组数据占一行。

Sample Input

9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0

Sample Output

6
5

解题思路: 搜索+枚举+递归

[cpp] view plaincopy

  1. /**  POJ 1011 sticks problem  **/
  2. //totalSticks    木棒的总数
  3. //len            正在尝试的原始木棍长度
  4. //unusedSticks   尚未拼接到len中的木棍
  5. //temp           当前正在拼接的木棍的剩余长度
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. bool is_original(int,int,int,int); //递归函数,判断某一长度是否可能是木棒的原始长度
  9. int cmp(const void *elem1,const void *elem2)  //qsort比较函数
  10. {
  11. return *(int *)elem2 - *(int *)elem1;   //降序排列
  12. }
  13. int sticks[100];  //存放木棍的数组
  14. bool used[100];    //记录木棍的使用情况
  15. int main()
  16. {
  17. //freopen("in.txt","r",stdin);
  18. int n;
  19. scanf("%d",&n);   //木棍数量
  20. while(n!=0)
  21. {
  22. int i,sum=0,len;
  23. for(i=0; i<n; i++)
  24. {
  25. used[i] = false;  //初始化 ,未使用
  26. scanf("%d",&(sticks[i])); //输入各个木棍长度
  27. sum += sticks[i];   //木棍总长度
  28. }
  29. qsort(sticks,n,sizeof(int),cmp);  //对木棍进行排序
  30. len = sticks[0];      //len的最小可能就是 最长的那一条木棍
  31. for(i=len; i<=sum; i++)  //按升序枚举原始木棒的可能长度
  32. {
  33. if(sum%i!=0)         //总长度不能整除可能长度 ,跳过
  34. continue;
  35. if(is_original(n,n,0,i))  //如果可能是原始长度
  36. {
  37. printf("%d\n",i);   //输出所需答案
  38. break;
  39. }
  40. }
  41. scanf("%d",&n);
  42. }
  43. return 0;
  44. }
  45. bool is_original(int totalSticks,int unusedSticks,int temp,int len)
  46. {
  47. int i;
  48. if(unusedSticks == 0&&temp == 0)  //如果剩余木棍为0,剩余长度为0,拼接完成
  49. return true;
  50. if(temp  == 0)         //当前拼接木棍剩余长度为0;
  51. temp = len;        //尝试新的一个原始长度
  52. for(i=0; i<totalSticks; i++) //从头到尾寻找可用的木棍
  53. {
  54. if(used[i]==true)      //用过了,跳过
  55. continue ;
  56. if(sticks[i]>temp)      //木棍大于剩余长度,跳过
  57. continue ;
  58. used[i] = true;         //标记为用过了
  59. if(is_original(totalSticks,unusedSticks-1,temp-sticks[i],len))
  60. return true;  //temp和unusedSticks 都减小 ,向下递归
  61. used[i] = false;     //退出上次尝试的木棍,准备开始下一个
  62. if(sticks[i]==temp||temp==len) //如果尝试的是某个木棍的第一位置或者最后位置
  63. break;      //并且导致失败,就不必尝试剩余的木棍了
  64. }
  65. return false;
  66. }

转载自:http://blog.csdn.net/skc361/article/details/10028743

这个回溯的剪枝实在是太精妙了,只有一句话,但是确实很难想到。这篇博客的代码我觉得是风格最优雅的一个了,就是里面的原理讲的不是太清楚,在这里记一下。

  1. if(sticks[i]==temp||temp==len) //如果尝试的是某个木棍的第一位置或者最后位置
  2. break;      //并且导致失败,就不必尝试剩余的木棍了

上面的代码里只有这一句话是剪枝用的。下面对这一句有深刻内涵的代码进行详解。

我们首先已经把木棍长度从大到小排序了,然后每次去探测木棍的时候也都是从大到小去探测。假设当前我们要判断L这个长度是否符合题意,木棍给了n个,总长度是sum。

  显然sum能被L整除,而且目标是让所有木棍组成sum/L个L长度的木棍。

  我们可以想象着sum/L个长度的东西为sum/L个桶,每个桶可以装长度为L的木棍。

  那么上面这段剪枝temp==len这个条件的具体意思就是:我们在判断搜到这个状态是否能够到达我们希望的终点时,我们需要去尝试每一个当前可以尝试的木棍,那么当我们放上一个木棍失败了,而且目前这根木棍又放在了桶底,那后面连试都不用试了,必定失败。因为剩下的木棍是一定的,当前还要从桶底开始放,所以这一次放哪一根木棍对结果没有影响,放这个木棍失败了,后面的必定也失败。

  那么上面这段剪枝sticks[i]==temp这个条件的具体意思是:当前这个木棍正好把一个桶填满了,然而失败,那么后面的都不用试了,必定失败。这里就是因为:我们的木棒是从大到小排序的。所以先试了长的木棒,不行。假设这根木棒长度为k,那么后面短的木棒肯定得能组成长度为k的木棒,否则这段空就填不起来了。然而如果后面的木棒能组成长度为k的填好这个桶的空,那这些木棒跟这根k木棒就等价(否则这个k木棒就没处放了),因此后面必定失败。(这里很绕,如果更详细的话可以分类讨论一下,总之就是不管什么情况后面都必定失败)

  真是神奇的剪枝……

时间: 2024-10-09 03:11:18

POJ1011 木棒(dfs+剪枝)的相关文章

EOJ1981 || POJ1011 经典dfs+剪枝+奇怪的数据

题目:EOJ1981 || POJ1011   经典dfs+剪枝+奇怪的数据 Description George took sticks of the same length and cut them randomly until all partsbecame at most 50 units long. Now he wants to return sticks to the originalstate, but he forgot how many sticks he had origi

poj1011(DFS+剪枝)

题目链接:https://vjudge.net/problem/POJ-1011 题意:给定n(<=64)条木棍的长度(<=50),将这些木棍刚好拼成长度一样的若干条木棍,求拼出的可能的最小长度. 思路:经典的DFS剪枝题,这道题的剪枝技巧很关键. 数据不大,可以想到枚举木棍所有可能的长度,然后利用dfs来查找所有可能的搭配情况,dfs的参数len表示当前的木棍长度,rest表示还需要的长度,num表示原始木棍中剩余没有匹配的数量,搜索的终止条件为rest==0&&num==0

POJ1011 Sticks DFS+剪枝

Description 乔治拿来一组等长的木棒,将它们随机地砍断,使得每一节木棍的长度都不超过50个长度单位.然后他又想把这些木棍恢复到为裁截前的状态,但忘记了初始时有多少木棒以及木棒的初始长度.请你设计一个程序,帮助乔治计算木棒的可能最小长度.每一节木棍的长度都用大于零的整数表示. Input 输入包含多组数据,每组数据包括两行.第一行是一个不超过64的整数,表示砍断之后共有多少节木棍.第二行是截断以后,所得到的各节木棍的长度.在最后一组数据之后,是一个零. Output 为每组数据,分别输出

POJ1011 (DFS+剪枝)

Sticks Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 129606   Accepted: 30388 Description George took sticks of the same length and cut them randomly until all parts became at most 50 units long. Now he wants to return sticks to the or

poj1011(深搜+剪枝)

题意:给m根木棍,将它们重新拼成n根一样长的木棍,并使得n尽量大(即每个新木棍尽量短). 解法:经典的搜索题目.从小到大枚举拼成的新木棍长度,每次枚举进行一次深搜.这题关键是如何剪枝. 1.当枚举的长度不能整除总长度的时候,剪枝:(这个很显然) 2.先将木棍从长到短排序,枚举时先尝试长的木棍.(先枚举长的可以使得搜索深度不至于过深) 3.深搜时,不要企图通过换掉一个新木棍的第一根来改变失败的局面(换掉第一根A,那么A也会在以后的新木棍中被使用,但是已经证明了A无法拼成了,所以不必再尝试下去了)

poj1011 Sticks DFS+回溯

转载请注明出处:http://blog.csdn.net/u012860063 题目链接:http://poj.org/problem?id=1011 Description George took sticks of the same length and cut them randomly until all parts became at most 50 units long. Now he wants to return sticks to the original state, but

[POJ 1011]Sticks(DFS剪枝)

Description George took sticks of the same length and cut them randomly until all parts became at most 50 units long. Now he wants to return sticks to the original state, but he forgot how many sticks he had originally and how long they were original

poj 1011/2362 dfs+剪枝(拼木棍)

题意:乔治拿来一组等长的木棒,将它们随机地砍断,使得每一节木棍的长度都不超过50个长度单位.然后他又想把这些木棍恢复到为裁截前的状态,但忘记了初始时有多少木棒以及木棒的初始长度.请你设计一个程序,帮助乔治计算木棒的可能最小长度.每一节木棍的长度都用大于零的整数表示.(2362是1011的特例,问一堆木棍能否拼成正方形) 思路:dfs+剪枝.其中剪枝具有相当的技巧性,其中一个地方的剪枝没有想到导致tle多次. 几个明显的剪枝点(设ans为最终的答案): 1.ans>=这堆木棍的最大长度 2.ans

ZOJ 1008 Gnome Tetravex (DFS + 剪枝)

Gnome Tetravex 题目:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=8 题意:有N*N个方格,每个方格分为上下左右四个部分,每个部分填数字.现在要求重排方块,使得每两个有边相连的方块对应的数字相同. 思路:就是一个简单的搜索,我想了个剪枝,将上下左右四个方向上每个数字对应的是哪几个方块记录下来,但是这个剪枝并没有起很大的作用,还是T了.后来才发现,如果有很多个方块是相同的,会重复搜索,所以需要将相同的方块一起处

UVA 10318 - Security Panel dfs 剪枝

UVA 10318 - Security Panel dfs 剪枝 ACM 题目地址:UVA 10318 - Security Panel 题意: 这题跟点灯的题目很像,点灯游戏选择一盏灯时会让它以及四周的灯改变状态. 但是我们有特殊的开开关技巧,它给出了改变状态的位置,而不是四周都改变. 问你从全部关着变成全部开着的最小开关步骤. 分析: 很明显,在一个位置上点两次或更多次是没有必要的,所以一个位置只有选择与不选择,用dfs即可,但如果暴力所有可能,复杂度是2^25,会超时,所以要剪枝. 由于