题目大意

乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过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. xhtml1-strict.dtd

    <!-- Extensible HTML version 1.0 Strict DTD This is the same as HTML 4 Strict except for changes ...

  2. C#之实体类

    一.为什么要用实体类? |  使程序简洁易懂,便于维护. |  暗合接口不变原则. |  体现面向对象思想.         举例说明: 不用实体类的三层 假如程序有所变动,需要增加一个参数,学生年龄 ...

  3. 自学Python七 爬虫实战一

    此文承接上文,让我们写一个简简单单的爬虫,循序而渐进不是吗?此次进行的练习是爬取前5页什么值得买网站中的白菜价包邮信息.包括名称,价格,推荐人,时间. 我们所需要做的工作:1.确定URL并获得页面代码 ...

  4. SQLServer2008 关于数值字段列的累计

    create table #temp20110610(     id int identity(1,1),     date varchar(8),     qty float) insert int ...

  5. C++对象的内存模型

    1. 普通对象模型 对象是如何在内存中布局的? 成员 存放位置 访问范围 非静态数据成员 每一个对象体内 为该对象专有 静态数据成员 程序的静态存储区内,只有一份实体 为该类所有对象共享 成员函数(静 ...

  6. 【sqli-labs】 less38 GET -Stacked Query Injection -String based (GET型堆叠查询字符型注入)

    这个直接用union select就可以 http://192.168.136.128/sqli-labs-master/Less-38/?id=0' union select 1,2,3%23 看一 ...

  7. python tips:作用域与名字空间

    Python具有静态作用域,变量的作用域由它定义的位置决定,而与调用的位置无关. a = 2 def f(): a = 2 第一行的a的作用域是全局作用域,作用于定义位置后面的所有位置. 第四行的a的 ...

  8. Restrictions.like("字段field","%表达式exp%");

    Restrictions.like("字段field","%表达式exp%");用hql语句就是 from table where field like '%e ...

  9. ptyhon获取app设备号、包名、activity

    直接上代码: import time import os import re import sys #------------------------------------------------- ...

  10. HDU 2268 How To Use The Car (数学题)

    题目 //做起来很艰辛,总结起来很简单... //注意步行速度可能比车的速度快.... //推公式要仔细,,,, //一道对我来说很搞脑子的数学题,,,,, //车先送第一个人上路,第二个人步行:中途 ...