传送门啦

一道经典的搜索剪枝题,不废话,步入正题。

分析:

一、输入时手动过滤不合法的情况

二、很明显我们要枚举把哪些棍子拼接成原来的长棍,而原始长度(原来的长棍的长度)都相等,因此我们可以在 $ dfs $ 外围枚举拼接后的每根长棍的长度。那枚举什么范围呢?

  其长度至少是最长的一根木棍,此时最长的这根木棍恰好单独组成原来的长棍。否则这根最长的木棍就无法自己或与其它木棍组成原来的长棍。其长度至多是所有木棍的长度之和,此时所有的木棍拼在一起恰好成为一根原来的长棍。

   $ dfs $ 一定会超时。所以我们考虑到当原始长度 不能被 所有木棍的长度之和 整除的话,这些木棍是拼不出整数根的(如果都拼成枚举的原来长棍的长度)。

  原始长度枚举到 所有木棍的长度之和/2 即可,因为此时所有木棍有可能拼成2根木棍,原始长度再大的话就只能是所有木棍拼成1根了,所以如果最后我们没有得出一个合法的方案,就直接把 $ sum $ 输出,即原来木棍是一根长度为 $ sum $ 的木棍。

三、下面就该考虑怎么搜索了。

dfs(int k , int last , int rest)分别表示正在拼第几根原来的长棍,使用的上一根木棍的编号,当前在拼的长棍还有多少长度未拼

光是这些肯定是不够的。。

四、各种优化。

1.一根长木棍肯定比几根短木棍拼成同样长度的用处小,可以说是短木棍灵活(??),所以对输入的所有木棍按长度从大到小排序,这样短木棍可以更加灵活地接在原始木棍上。

2.根据1,将输入的木棍排好序后,当用木棍 $ i $ 拼合原始长棍时,从第 $ i + 1 $ 根木棍开始往后搜。在分析二中我们也讨论了原始木棍的范围了,大于等于最长的一根木棍。

3.当 $ dfs $ 返回拼接失败,需要更换当前使用的木棍时,不要再用与当前木棍的长度相同的木棍,因为当前木棍用了不行。可以用 $ next[ ] $ 预处理出了排序后每根木棍后面的最后一根与这根木棍长度相等的木棍,它的下一根木棍就是第一根长度不相等的木棍了。

4.只找木棍长度不大于未拼长度rest的所有木棍。可以根据木棍长度的单调性来二分找出第一个木棍长度不大于未拼长度 $ rest $ 。它后面的木棍一定都满足这个条件。

5.由于是从小到大枚举 原始长度,因此第一次发现的答案就是最小长度。dfs中只要发现所有的木棍都凑成了若干根原长度的长棍(容易发现 凑出长棍的根数=所有木棍的长度之和/原始长度),立刻一层层退出dfs,不用滞留,退到dfs外后直接输出原始长度并结束程序。

最后一个优化,也是最难最不好想最不好理解的一个。

其实如果不加最后这一个优化可以得到78分,考场上也是一个比较可观的分数。

6.还有一个难想却特别特别重要的优化:如果当前长棍剩余的未拼长度等于当前木棍的长度或原始长度,继续拼下去时却失败了,就直接回溯并改之前拼的木棍。有些人不太明白这个优化,这里简单说一下:

 当前长棍剩余的未拼长度等于当前木棍的长度时,当前木棍明显只能自组一根长棍,但继续拼下去却失败,说明这根木棍不能自组?!这根木棍不自组就没法用上了,所以不用搜更短的木棍了,直接回溯,改之前的木棍;

 当前长棍剩余的未拼长度等于原始长度时,说明这根原来的长棍还一点没拼,现在正在放入一根木棍。很明显,这根木棍还没有跟其它棍子拼接,如果现在拼下去能成功话,它肯定是能用上的,即自组或与其它还没用的木棍拼接。但继续拼下去却失败,说明现在这根木棍不能用上,无法完成拼接,所以直接回溯,改之前的木棍。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std; inline int read(){
//快速读入
char ch = getchar();
int f = 1 , x = 0;
while(ch > '9' || ch < '0'){if(ch == '-')f = -1;ch = getchar();}
while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + ch - '0';ch = getchar();}
return x * f;
} int n,x,a[70],tot;
int sum,m,len,next[70];
bool ok,used[70]; bool cmp(int a,int b){return a > b;} void dfs(int k,int last,int rest){
int i;
if(!rest){
if(k == m){
ok = 1;
return ;
}
for(i=1;i<=tot;i++)
if(!used[i]) break;
used[i] = 1;
dfs(k + 1 , i , len - a[i]);
used[i] = 0;
if(ok) return;
}
int l = last + 1 , r = tot , mid;
while(l < r){
//二分查找,优化4
mid = (l + r) >> 1;
if(a[mid] <= rest) r = mid;
else l = mid + 1;
}
for(i=l;i<=tot;i++)
if(!used[i]){
used[i] = 1;
dfs(k , i , rest - a[i]);
used[i] = 0;
if(ok) return ; //找到答案直接退出
if(rest == a[i] || rest == len) return ;
//优化6
i = next[i];
if(i == tot) return;
}
} int main(){
n = read();
for(int i=1;i<=n;i++){
x = read();
if(x <= 50){
a[++tot] = x;
sum += x;
}
}
sort(a + 1 , a + 1 + tot , cmp);//优化1
next[tot] = tot;
for(int i=tot-1;i>0;i--){
//优化 3
if(a[i] == a[i+1]) next[i] = next[i+1];
else next[i] = i;
}
for(len=a[1];len<=sum/2;len++){ //从最长的开始搜,优化2
//枚举到sum / 2,分析二
if(sum % len != 0) continue; //无法整除直接过滤,分析二
m = sum / len;
ok = 0;
used[1] = 1;
dfs(1 , 1 , len - a[1]);
used[1] = 0;
if(ok){
printf("%d\n",len);
return 0;
}
}
printf("%d\n",sum);
return 0;
}

洛谷P1120 小木棍(升级版)的更多相关文章

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

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

  2. 洛谷P1120 小木棍

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

  3. 【题解】洛谷P1120 小木棍(搜索+剪枝+卡常)

    洛谷P1120:https://www.luogu.org/problemnew/show/P1120 思路 明显是搜索题嘛 但是这数据增强不是一星半点呐 我们需要N多的剪枝 PS:需要先删去超出50 ...

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

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

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

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

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

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

  7. 洛谷P1120 小木棍 [搜索]

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

  8. 洛谷 P1120 小木棍 dfs+剪枝

    Problem Description [题目链接] https://www.luogu.com.cn/problem/P1120 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不 ...

  9. 洛谷P1120小木棍[DFS]

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

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

    题目大意:有一些同样长的木棍,被切割成几段(长$\leqslant$50).给出每段小木棍的长度,找出原始木棍的最小可能长度. 题解:dfs C++ Code: #include<cstdio& ...

随机推荐

  1. CSS3 grayscale滤镜+SVG使图片变黑白实例页面

    CSS代码: .gray { -webkit-filter: grayscale(100%); -moz-filter: grayscale(100%); -ms-filter: grayscale( ...

  2. 编译geth报错的解决方法 make: *** [geth] 错误 1

    在centos下安装了go1.9.1版本,编译go-ethereum时报错: [root@localhost go-ethereum]# make gethbuild/env.sh go run bu ...

  3. 将SQL Server账户对应到Windows系统账户

    应用场景举例: SQL Server账户要访问外部资源,例如所拥有的Job要访问文件系统,而此文件系统需要Windows账户才有权限. 步骤: 1.       服务器新建凭据(Credentials ...

  4. 拥抱Service Fabric —— 目录

    理解分布式 经典分布式系统设计 云时代分布式系统演进 Service Fabric基础概念 Node, Application, Service, Partition/Replicas Partiti ...

  5. 解决ImportError: cannot import name HTTPSHandler

    /usr/local/python3.5/bin/pip3.5 install flask 的时候遇到了cannot import name HTTPSHandler 1. 原因在于openssl,o ...

  6. Java基础-IO流对象之内存操作流(ByteArrayOutputStream与ByteArrayInputStream)

    Java基础-IO流对象之内存操作流(ByteArrayOutputStream与ByteArrayInputStream) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.内存 ...

  7. memcmp 和 memcpy使用

    #include <iostream> #include <cstring> #include <cstdlib> #include <cstdio> ...

  8. js事件的捕获和冒泡阶段

    讨论的主要是两个事件模型:IE事件模型与DOM事件模型 IE内核浏览器的事件模型是冒泡型事件(没有捕获事件过程),事件句柄的触发顺序是从ChildNode到ParentNode. <div id ...

  9. [hadoop]mapreduce原理简述

    1.用于map的输入,先将输入数据切分成相等的分片,为每一个分片创建一个map worker,这里的切片大小不是随意订的,一般是与HDFS块大小一致,默认是64MB,一个节点上存储输入数据切片的最大s ...

  10. 环境变量ANDROID_SDK_HOME的作用

    默认情况下,开发者创建的AVD(Android Virtual Device)存放在家目录的.android下. 如果是Linux,其路径就是 /home/<your_user_name> ...