传送门啦

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

分析:

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

二、很明显我们要枚举把哪些棍子拼接成原来的长棍,而原始长度(原来的长棍的长度)都相等,因此我们可以在 $ 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. Django JSON 时间

    在views.py中导入: from django.core.serializers.json import DjangoJSONEncoder 在返回JSON数据时调用: return HttpRe ...

  2. 栈(C语言实现)

    栈是一种线性数据结构,顺序可能是 LIFO(后进先出)或 FILO(先进先出). 堆栈主要有三个基本操作: 1.push,把元素压入栈 2.pop,从栈中弹出元素(同时从栈中移除),最后加入的第一个被 ...

  3. 由支付宝当面付引发的NatApp方便调试回调

    http://blog.csdn.net/xunxianren007/article/details/54954520 这篇文章写的很好,很详细. 回调理解: 所谓回调:就是A类中调用B类中的某个方法 ...

  4. P2207 Photo

    P2207 Photo 题目描述 Framer Jhon 打算给他的N头奶牛照相,( 2 <= N <= 1 000 000 000) . 他们排成一条线,并且依次取1~N作为编号. 每一 ...

  5. Java 守护线程概述

    原文出处: 朱小厮 Java的线程分为两种:User Thread(用户线程).DaemonThread(守护线程). 只要当前JVM实例中尚存任何一个非守护线程没有结束,守护线程就全部工作:只有当最 ...

  6. Java基础-面向对象第二特征之继承(Inheritance)

    Java基础-面向对象第二特征之继承(Inheritance) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.继承的概述 在现实生活中,继承一般指的是子女继承父辈的财产.在程序 ...

  7. 八、Kafka总结

    一 Kafka概述 1.1 Kafka是什么 在流式计算中,Kafka一般用来缓存数据,Storm通过消费Kafka的数据进行计算. 1)Apache Kafka是一个开源消息系统,由Scala写成. ...

  8. 图片截取插件Cropper

    自己仿照github上的例子写的demo,github上的例子太抽象了,自己写的最适合自己,通俗易懂. <!DOCTYPE html> <html> <head> ...

  9. ASP.NET项目与IE10、IE11不兼容的解决办法

    1.解决办法 机器级别修复, 服务器所有ASP.NET程序受益 需要去微软下载对应asp.NET版本的修补程序 .NET 4 -http://support.microsoft.com/kb/2600 ...

  10. bzoj 2111 [ZJOI2010]Perm 排列计数(DP+lucas定理)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2111 [题意] 给定n,问1..n的排列中有多少个可以构成小根堆. [思路] 设f[i ...