POJ 1011 拯救少林神棍(Sticks)

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

AC代码

#include <iostream>
#include <algorithm>
#include <vector>
#include <memory.h>
using namespace std;
int N, L;//定义木棒数目和假设棍子的长度
vector<int> anLength;//用于存放所有木棒长度
int anUsed[65];//是否用过的标记且全局变量自动初始化为0即木棒未使用过
int nLastStickNo;//用于储存最近那根拼上去的木棒下标
bool DFS(int nUnusedSticks, int nLeft);int main()
{while(1) {cin >> N;//输入所有木棒根数 if( N == 0 ) break;int nTotalLen = 0;//所有木棒总长度 anLength.clear();for(int i = 0;i < N; i++) {int n;//单根木棒长度 cin >> n;anLength.push_back(n);nTotalLen += anLength[i];}sort(anLength.begin(),anLength.end(),greater<int>()); //要使木棒从长到短进行尝试,好比天平上称物品,加砝码也是从大到小试 for(L = anLength[0];L <= nTotalLen / 2; L++) //从最长的木棒开始从长到短依次枚举木棍长度L{if(nTotalLen % L)    continue;
//能整除代表你可以拼成若干组等长木棍,不能整除代表你可能会拼得几根等长的棍子,但最后总会有木棒剩下  memset(anUsed, 0, sizeof(anUsed));//初始化anUsed数组元素为0if(DFS(N, L)) //s根可用的木棒,L为假设的木棍长度 {cout << L << endl;break;}}if(L > nTotalLen / 2) cout << nTotalLen << endl;
//如果枚举出的木棍长度大于所有木棒总长度的一半那么就没法拼成两根或更多木棍,故此木棒总长度便是最短的木棍长度 } return 0;
}bool DFS(int nUnusedSticks, int nLeft)
{// nLeft表示当前正在拼的棍子和 L 比还缺的长度if(nUnusedSticks == 0 && nLeft == 0) return true;//没有木棒剩下且没有哪根棍子缺长度,任务完成! if(nLeft == 0) //木棒剩下且没有哪根棍子缺长度,证明一根木棍刚刚拼完,再新拼一根 nLeft = L; //开始拼新的一根木棍 //剪枝4: int nStartNo = 0;if(nLeft != L) //拼这根棍子的木棒不是第一根木棒 nStartNo = nLastStickNo + 1;//从上一次选取的后面进行选取,保证了拼木棍时木棒先长后短 for(int i = nStartNo;i < N; i++)    {if(!anUsed[i] && anLength[i] <= nLeft) {//剪枝1: if(i > 0){if(anUsed[i-1] == false && anLength[i] == anLength[i-1])continue;//若木棒之前一根木棒用过或和前一根等长那么就不用这根木棒}  anUsed[i] = 1;nLastStickNo = i;if (DFS(nUnusedSticks - 1, nLeft - anLength[i]))return true;else{anUsed[i] = 0;//说明本次不能用这第i根木棒但这第i根以后还有用//剪枝2和3: if(nLeft == L||anLength[i] == nLeft) return false;//2:这第i根木棒是这根棍子的第一根木棒,但anUse[i]=0表示这第i根木棒不能用,故拼接失败  //3:这第i根木棒是这根棍子最后一根木棒,但anUse[i]=0表示这第i根木棒不能用,故拼接失败 }}}return false;//所有的木棒都拿来试过了,但棍子长度不合理故return false
}

DEV C++测试结果:

算法解析

DFS的基本递推关系:

bool DFS(int R, int M)
{if( R == 0 && M == 0)return true; //拼接任务完成//若能找到一根长度不超过M的木棒, 假设长为S,拼在当前棍子上,然后:DFS(R – 1,M - S);//如果找不到:return false;
}

剪枝 1

不要在同一个位置多次尝试相同长度的木棒。
即:如果某次拼接选择长度为S 的木棒,导致最终失败,则在同一位置尝试下一根木棒时,要跳过所有长度为S 的木棒。

剪枝 2

如果由于以后的拼接失败,需要重新调整第i根棍子的拼法,则不会考虑替换第i根棍子中的第一根木棒(换了也没用)。如果在不替换第一根木棒的情况下怎么都无法成功,那么就要推翻第i−1i-1i−1根棍子的拼法。如果不存在第i−1i-1i−1根棍子,那么就推翻本次假设的棍子长度,尝试下一个长度。
若棍子 iii 如下拼法导致最后不能成功:

可以考虑把木棒2,3换掉重拼棍子i,但是把2,3都去掉后,换1是没有意义的。

为什么替换第i根棍子的第一根木棒是没用的?

因为假设替换后能全部拼成功,那么这被换下来的第一根木棒,必然会出现在以后拼好的某根棍子k中。那么我们原先拼第i根棍子时, 就可以用和棍子k同样的构成法来拼,照这种构成法拼好第i根棍子,继续下去最终也应该能够全部拼成功。

剪枝 3

不要希望通过仅仅替换已拼好棍子的最后一根木棒就能够改变失败的局面。

假设由于后续拼接无法成功,导致准备拆除的某根棍子 iii 如下:

将 3 拆掉,留下的空用其他短木棒来填是徒劳的!

假设替换3后最终能够成功,那么3必然出现在后面的某个棍子 kkk 里。将棍子 kkk 中的3和棍子 iii 中用来替换3的几根木棒对调,结果当然一样是成功的。这就和i原来的拼法会导致不成功矛盾

剪枝 4

拼每一根棍子的时候,应该确保已经拼好的部分,长度是从长到短排列的,即拼的过程中要排除类似下面这种情况:

未完成的棍子 iii


木棒3 比木棒2长,这种情况的出现是一种浪费。因为要是这样往下能成功,那么2, 3 对调的拼法肯定也能成功。由于取木棒是从长到短的,所以能走到这一步,就意味着当初将3放在2的位置时,是不成功的
排除办法:每次找一根木棒的时候,只要这不是一根棍子的第一条木棒,就不应该从下标为0的木棒开始找,而应该从刚刚(最近)接上去的那条木棒的下一条开始找。这样,就不会往2后面接更长的3了

为此,要设置一个全局变量 nLastStickNo ,记住最近拼上去的那条木棒的下标。

DFS:拯救少林神棍(Sticks)[四轮剪枝操作]相关推荐

  1. MobileNetV3基于NNI剪枝操作

    NNI剪枝入门可参考:nni模型剪枝_benben044的博客-CSDN博客_nni 模型剪枝 1.背景 本文的剪枝操作针对CenterNet算法的BackBone,即MobileNetV3算法. 该 ...

  2. DFS——拯救OIBH总部

    拯救OIBH总部(来源于http://acm.qust.edu.cn/problem.php?id=1101) OIBH被突来的洪水淹没了> .< 还好OIBH总部有在某些重要的地方起一些 ...

  3. tensorflow 网络修剪 剪枝操作

    背景知识 模型剪枝(Model Pruning)是一种模型压缩方法,对深度神经网络的稠密连接引入稀疏性,通过将"不重要"的权值直接置零来减少非零权值数量,其历史可追溯到上世纪 90 ...

  4. 枚举+递归+DFS基础模板类算法总结

    :这几天刷这类型题有点难,把这些基础的总结一下,理一下思路,这里感谢一下b站up主:一只会code的小金鱼 ,学到了好多思路 1. 递归实现指数型枚举: eg:从 1∼n这 n个整数中随机选取任意多个 ...

  5. 《图壳》免费超好用的图床,图片最稳定的家

    在线直达地址: https://imgkr.com/ 一张图教程 t030-imgkr 图1地址:https://static01.imgkr.com/temp/639dddf9733b45e4a25 ...

  6. 暴力搜索---新技能get

    最近新学习了一种新的求解的方法,就是暴力搜索,在通常做题没有很明确的思路的时候,通常都会采用的一种方式. 我们知道,一个问题的解空间通常对应的是一棵树的方式进行组织的,那么我们可以通过根据题目中的条件 ...

  7. 程序设计与算法MOOC021:鸣人与佐助(C++DFS、剪枝)

    题目要求 已知一张地图(以二维矩阵的形式表示)以及佐助和鸣人的位置.地图上的每个位置都可以走到,只不过有些位置上有大蛇丸的手下,需要先打败大蛇丸的手下才能到这些位置.鸣人有一定数量的查克拉,每一个单位 ...

  8. DFS(深度优先搜索)详解(概念讲解,图片辅助,例题解释,剪枝技巧)

    目录 那年深夏 引入 1.什么是深度优先搜索(DFS)? 2.什么是栈? 3.什么是递归? 图解过程 问题示例 1.全排列问题 2.迷宫问题 3.棋盘问题(N皇后) 4.加法分解 模板 剪枝 1.简介 ...

  9. 洛谷P1118 [USACO06FEB]数字三角形 Backward Digit Su(dfs剪枝)

    题目描述 FJ and his cows enjoy playing a mental game. They write down the numbers from 11 toN(1 \le N \l ...

  10. Poj 1011:sticks通俗易懂 大法师+强力剪枝

    题目描述 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过 50 50 50. 现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度. 给出每段小 ...

最新文章

  1. vsftpd配置文件详解
  2. 奥西300工程机服务器装系统,奥西工程机ftp服务器登录
  3. 探讨BI可视化下的旅游大数据分析,你的钱都花哪了?
  4. date时区 es logstash_es-日志存储-Logstash 介绍
  5. 全面了解Nginx到底能做什么
  6. MySql like 查询 变向写法(不用like 完成like查询)
  7. 牛客网【每日一题】Shortest Path 4月3日题目精讲 DFS
  8. 远控免杀专题(21)-HERCULES免杀
  9. 一步步编写操作系统 61 任务状态段 TSS
  10. 如何正确地使用#region指令
  11. web开发-引用Google font library-学习笔记四
  12. UE-战斗无止境的复刻
  13. HTML+CSS基础知识简单版
  14. 虚拟机的虚拟化如何开启?
  15. [转载]999只纸鹤,999个爱你的理由…… 【1】
  16. 随笔7:R语言绘图黑白印刷风格芯片包
  17. 有1分,2分,5分,10分四种硬币,每种硬币数量无限,给定n分钱(n <= 100000),有多少中组合可以组成n分钱?
  18. 告诉大家一个不买到假U盘的方法
  19. zencart忘记后台密码的解决办法
  20. 封装-private关键字

热门文章

  1. JeecgBoot集成DataV组件库
  2. 基于ECharts数据可视化案例--世界疫情实时展示
  3. php jwt payload,php实现JWT(json web token)鉴权实例详解
  4. Java - 使用Cipher类实现加密(RSA)
  5. RHEL8.4系统镜像
  6. 科研必备之图像局部区域放大——画中画形式
  7. 深圳市社会医疗保险门诊大病管理办法
  8. matlab标记最大的连通区域,Matlab得到二值图像中最大连通区域
  9. 如何在百度和各大网站搜索到自己的文章
  10. 网页游戏对java的技术要求_网页制作谈谈什么技术是Java开发网页游戏的必要条件呢?怎样在微信公众平台上制作5级游戏?...