题目大意

乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过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对应木棍)木棍们。
  • 排序变换:将每个组合成的木棍组内的木棍从大到小排列,然后将木棍组按照木棍组中的第一个木棍的长度从大到小排列。

隐式图中,每个节点的值为:

  1. 这是前几个木棍
  2. 到当前木棍的排列。

每条边的值为新增的木棍的长度。

但是此朴素算法效率很低。

整体算法的改变

当Dfs到当前木棍时,如果前(当前木棍的长度前缀和/eachLen)个原始木棍不能凑成,则Dfs下去的结果肯定不成立。所以Dfs到当前节点时,应当保证前(当前木棍的长度前缀和/eachLen)个原始木棍能凑成(条件*)。

怎么表示当前木棍的排列

因为保证了前(当前木棍的长度前缀和/eachLen)个原始木棍能凑成,所以我们不用记录组合成的木棍组的具体排列,只要记录哪些木棍访问过了即可。而对于第(当前木棍的长度前缀和/eachLen+1)个原始木棍(以后简称当前原始木棍),我们要达到的,是使得以后在处理后一个原始木棍时,仍然满足条件*。换句话说,就是把当前处理的原始木棍拼满。所以我们要记录一个rest,表示要凑成当前原始木棍,还需要的小木棍的长度为多少。这样,每个节点的值就变成了:

  1. 当前是前几个木棍(cnt)。
  2. rest.
  3. 每个木棍是否访问过的集合。

剪枝

排序

假设:对于每个能凑成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 小木棍【数据加强版】 暴力剪枝的更多相关文章

  1. 洛谷P1120 小木棍 [数据加强版](搜索)

    洛谷P1120 小木棍 [数据加强版] 搜索+剪枝 [剪枝操作]:若某组拼接不成立,且此时 已拼接的长度为0 或 当前已拼接的长度与刚才枚举的长度之和为最终枚举的答案时,则可直接跳出循环.因为此时继续 ...

  2. 洛谷 P1120 小木棍[数据加强版]

    这道题可能是我做过的数据最不水的一道题-- 题目传送门 这题可以说是神剪枝,本身搜索并不算难,但剪枝是真不好想(好吧,我承认我看了题解)-- 剪枝: 用桶来存储木棍 在输入的时候记录下最长的木棍和最短 ...

  3. P1120 小木棍 [数据加强版] 回溯法 终极剪枝

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

  4. Luogu P1120 小木棍 [数据加强版] 来来来我们一起来剪枝,剪枝,剪枝、、、

    好啊...太棒了... dfs(拼到第几根木棍,这根木棍剩余长度,上一根木棍的位置) len是木棍的长度,cnt是木棍的个数 震撼人心的剪枝: 1.枚举长度从最大的木棍开始,直到sum/2,因为之后只 ...

  5. 洛谷 P1120 小木棍 [数据加强版]解题报告

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

  6. 洛谷——P1120 小木棍 [数据加强版]

    P1120 小木棍 [数据加强版] 题目描述 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过5050. 现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍 ...

  7. 题解 P1120 【小木棍 [数据加强版]】

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

  8. P1120 小木棍 [数据加强版]

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

  9. P1120 小木棍 [数据加强版](poj 1011)

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

随机推荐

  1. Spring Boot (2) Restful风格接口

    Rest接口 动态页面jsp早已过时,现在流行的是vuejs.angularjs.react等前端框架 调用 rest接口(json格式),如果是单台服务器,用动态还是静态页面可能没什么大区别,如果服 ...

  2. PL/SQL实现JAVA中的split()方法的小例子

    众所周知,java中为String类提供了split()字符串分割的方法,所以很容易将字符串以指定的符号分割为一个字符串数组.但是在pl/sql中并没有提供像java中的split()方法,所以要想在 ...

  3. CDC之fast->slow (1)

    Sampling slower signals into faster clock domains causes fewer potential problems than sampling fast ...

  4. 用SQL Server查询所有数据并显示

    利用SQL Server查询数据,并把所有数据显示在页面上. "%> <%@page import="jdbc.sqlServer"%> <%@p ...

  5. dubbo之上下文信息

    上下文信息 上下文中存放的是当前调用过程中所需的环境信息.所有配置信息都将转换为 URL 的参数,参见 schema 配置参考手册 中的对应URL参数一列. RpcContext 是一个 Thread ...

  6. CXF-JAX-RS开发(二)spring整合CXF-JAX-RS

    一.创建maven工程[Packaging:war] 1.目录结构 2.坐标书写 二.导入依赖和tomcat服务器插件 <dependencies> <!-- CXF --> ...

  7. 在jboss上部署web应用

    1.JBoss介绍 JBoss完全实现了J2EE的服务栈: EJB (Enterprise JavaBeans) JMS (Java Message Service) JTS/JTA (Java Tr ...

  8. 数据清理,预处理 pandas dataframe 操作技巧 总结

    dsoft2 = data1.loc[(data1['程'] == "轻") | (data1['程'] == "中")]设置x下标plt.xticks(np. ...

  9. eas设置时分秒

  10. safari浏览器click事件要点击两次才有响应出现闪烁

    闪烁问题 由于在iOS Safari上click事件存在300ms响应延时,所以为touch事件添加样式,会和click事件默认样式叠加而产生闪烁问题. 因为ios safari浏览器中对触摸事件的响 ...