luogu1120 小木棍【数据加强版】 暴力剪枝
题目大意
乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50。现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。
总体思路
令totLen为所有小木棍总长度,枚举每一个原始木棍的可能长度eachLen(sumLen%eachLen==0),判断那些原始木棍能否全部由小木棍凑成。
判断过程的朴素算法为:枚举原始木棍的每一种排列方式,看看这样能否凑成totLen/eachLen个原始木棍。
一些定义
- 到当前木棍的排列:Dfs到当前木棍时,将Dfs访问过的木棍按访问的顺序排成一行,这一行木棍的排列 叫做到当前木棍的排列。
- 某一木棍的长度前缀和:当前木棍排列中的第一个木棍到“某一”木棍之间(包括这两个木棍)木棍的总长度。
- 前n个木棍能凑成:在到当前木棍的排列中,对于每个k∈[1, N]∩Z,都能在到当前木棍排列中找到一个木棍,使得该木棍的长度前缀和=k*eachLen。
- 组合成的木棍组:在上述每个k∈[1,N]∩Z,k对应的木棍和k-1对应的木棍之间的(不包括k-1对应木棍,包括k对应木棍)木棍们。
- 排序变换:将每个组合成的木棍组内的木棍从大到小排列,然后将木棍组按照木棍组中的第一个木棍的长度从大到小排列。
隐式图中,每个节点的值为:
- 这是前几个木棍
- 到当前木棍的排列。
每条边的值为新增的木棍的长度。
但是此朴素算法效率很低。
整体算法的改变
当Dfs到当前木棍时,如果前(当前木棍的长度前缀和/eachLen)个原始木棍不能凑成,则Dfs下去的结果肯定不成立。所以Dfs到当前节点时,应当保证前(当前木棍的长度前缀和/eachLen)个原始木棍能凑成(条件*)。
怎么表示当前木棍的排列
因为保证了前(当前木棍的长度前缀和/eachLen)个原始木棍能凑成,所以我们不用记录组合成的木棍组的具体排列,只要记录哪些木棍访问过了即可。而对于第(当前木棍的长度前缀和/eachLen+1)个原始木棍(以后简称当前原始木棍),我们要达到的,是使得以后在处理后一个原始木棍时,仍然满足条件*。换句话说,就是把当前处理的原始木棍拼满。所以我们要记录一个rest,表示要凑成当前原始木棍,还需要的小木棍的长度为多少。这样,每个节点的值就变成了:
- 当前是前几个木棍(cnt)。
- rest.
- 每个木棍是否访问过的集合。
剪枝
排序
假设:对于每个能凑成totLen/eachLen个原始木棍的排列,对其进行排序变换。对于每种排列,变换过后的结果与原来是等价的,但是多个排列经过变换后变成了同一种排列。这样,我们规定每一种可行排列都满足每个木棍组内的木棍长度递减,木棍组的第一个木棍的长度递减,这样就缩小了解空间。具体操作为:运算前先将每个木棍从大到小排序。Dfs当前木棍时记录当前从数组中哪个下标处开始,备选下一个木棍就从当前位置枚举。
为什么排序从大到小
因为如果一个木棍的排列不能凑成凑成totLen/eachLen个原始木棍,长度长的木棍在先可以较早地判断出该排列不满足要求。
排除显然不成立的情况
位于当前木棍扩展时,当前备选木棍与上一次的备选木棍长度相等
遇到本情况显然跳过,因为Dfs到上一次所选节点时,其备用木棍组合包括了Dfs到当前备选木棍时的木棍组合。前者都不成立,后者当然不成立。
(当前木棍的长度前缀和%eachLen)==0时,第一个备选木棍便无法(通过放入第(当前木棍的长度前缀和/eachLen+1)个原始木棍)来凑成所有原始木棍。
本情况也要跳过。假设不跳过,则这第一个备选木棍就要在后后个原始木棍中再次出现。对以后的组合成的木棍组进行排序变换,则那个木棍又出现在了第(当前木棍的长度前缀和/eachLen+1)原始木棍中,这与当前情况矛盾。
当前木棍的长度=eachLen,还不能凑成所有原始木棍
跳过,证明与上同理。
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cassert>
using namespace std; const int MAX_STICK = 70;
bool Vis[MAX_STICK];
int Len[MAX_STICK];
int Tot;
#define LOOP(i, n) for(int i=1; i<=n; i++)
#define LoopFrom(i, m, n) for(int i=m; i<=n; i++) bool cmp(int a, int b)
{
return a > b;
} bool Dfs(int cnt, int rest, int eachLen, int begin)
{
if (cnt == Tot)
return rest == 0;
if (rest == 0)
{
rest = eachLen;
begin = 1;
}
int prevLen = -1;
LoopFrom(next, begin, Tot)
{
if (Len[next] == prevLen || Vis[next] || rest-Len[next]<0)
continue;
prevLen = Len[next];
Vis[next] = true;
if (Dfs(cnt + 1, rest - Len[next], eachLen, next+1))
return true;
else if (rest == eachLen || rest-Len[next]==0)
{
Vis[next] = false;
return false;
}
else
{
Vis[next] = false;
continue;
}
}
return false;
} int main()
{
#ifdef _DEBUG
freopen("c:\\noi\\source\\input.txt", "r", stdin);
#endif
Tot = 0;
int tempTot;
scanf("%d", &tempTot);
int maxLen = 0, sumLen = 0, stickLen;
while(tempTot--)
{
scanf("%d", &stickLen);
if (stickLen <= 50)
{
Len[++Tot] = stickLen;
maxLen = max(maxLen, stickLen);
sumLen += stickLen;
}
}
sort(Len + 1, Len + Tot + 1, cmp);
LoopFrom(eachLen, maxLen, sumLen)
{
if (sumLen%eachLen)
continue;
memset(Vis, false, sizeof(Vis));
if (Dfs(0, 0, eachLen, 1))
{
printf("%d\n", eachLen);
break;
}
}
return 0;
}
luogu1120 小木棍【数据加强版】 暴力剪枝的更多相关文章
- 洛谷P1120 小木棍 [数据加强版](搜索)
洛谷P1120 小木棍 [数据加强版] 搜索+剪枝 [剪枝操作]:若某组拼接不成立,且此时 已拼接的长度为0 或 当前已拼接的长度与刚才枚举的长度之和为最终枚举的答案时,则可直接跳出循环.因为此时继续 ...
- 洛谷 P1120 小木棍[数据加强版]
这道题可能是我做过的数据最不水的一道题-- 题目传送门 这题可以说是神剪枝,本身搜索并不算难,但剪枝是真不好想(好吧,我承认我看了题解)-- 剪枝: 用桶来存储木棍 在输入的时候记录下最长的木棍和最短 ...
- P1120 小木棍 [数据加强版] 回溯法 终极剪枝
题目描述 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过5050. 现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度. 给出每段小木棍的长度 ...
- Luogu P1120 小木棍 [数据加强版] 来来来我们一起来剪枝,剪枝,剪枝、、、
好啊...太棒了... dfs(拼到第几根木棍,这根木棍剩余长度,上一根木棍的位置) len是木棍的长度,cnt是木棍的个数 震撼人心的剪枝: 1.枚举长度从最大的木棍开始,直到sum/2,因为之后只 ...
- 洛谷 P1120 小木棍 [数据加强版]解题报告
P1120 小木棍 [数据加强版] 题目描述 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50. 现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它 ...
- 洛谷——P1120 小木棍 [数据加强版]
P1120 小木棍 [数据加强版] 题目描述 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过5050. 现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍 ...
- 题解 P1120 【小木棍 [数据加强版]】
题面 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50. 现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度. 给出每段小木棍的长度,编程帮 ...
- P1120 小木棍 [数据加强版]
题目描述 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50. 现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度. 给出每段小木棍的长度,编 ...
- P1120 小木棍 [数据加强版](poj 1011)
题目描述 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50. 现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度. 给出每段小木棍的长度,编 ...
随机推荐
- 解决QQ未启用状态,QQ留言图标未启用
最近由于腾讯升级QQ一些东西,导致QQ图标成未启用状态:如图 解决方法,到腾讯此站点登陆一下即可, http://wp.qq.com/set.html 另外设置 没有保存按钮,如果选择完全公开,到自己 ...
- 前端HTML5思维导图笔记
看不清的朋友右键保存或者新窗口打开哦!热爱学习前端,喜欢我可以关注我,更多的思维导图笔记
- Android开机图片替换
Android开机图片替换 Android从启动到进入Launcher一共会展示三张图片,如果只是更换静态图则更换这三张图片即可,要想换成动画那就要另外操作. 首先查找这个文件: /bootab ...
- ListView使用、ListView优化和遇到的问题
1.先写遇到的问题: a.ListView只显示一个item. listview只显示一个item,并且做了listview的点击事件监听打印 Bean 对象的属性和哈希值,发现只有显示的那个 Bea ...
- 在命令提示符窗口下(cmd)使用指令操作并编译java代码,运行java编译代码
使用cmd操作java代码,编译.java文件,运行.class文件. 操作步骤: 1:创建一个文件夹: 例如:在e盘根目录(\)下面创建一个名为Hello的文件夹: 使用md指令:如图 在e盘中会生 ...
- for 循环 乘法口诀表
用for循环写乘法口诀表: for(var i = 1; i <= 9; i++) { var c=''; for(var x = 1; x <= i; x++) { c=c+x+' ...
- 配置 Sybase数据源
1.Start-- > All Programs -- > Sybase - -> Connectivity --> click ‘Open Client Directory ...
- VS2013配置编译Caffe-Win10_X64
原文链接:http://blog.csdn.net/joshua_1988/article/details/45036993 有少量修改................ 2014年4月的时候自己在公司 ...
- MVC POST请求后执行javascript代码
[HttpPost] public ActionResult PostTest() { //你的业务代码 //...... //要执行的js string js = "window.loca ...
- python jieba分词(添加停用词,用户字典 取词频
中文分词一般使用jieba分词 1.安装 pip install jieba 2.大致了解jieba分词 包括jieba分词的3种模式 全模式 import jieba seg_list = jieb ...