洛谷P1120 小木棍(升级版)
传送门啦
一道经典的搜索剪枝题,不废话,步入正题。
分析:
一、输入时手动过滤不合法的情况
二、很明显我们要枚举把哪些棍子拼接成原来的长棍,而原始长度(原来的长棍的长度)都相等,因此我们可以在 $ 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 小木棍(升级版)的更多相关文章
- 洛谷P1120 小木棍 [数据加强版](搜索)
洛谷P1120 小木棍 [数据加强版] 搜索+剪枝 [剪枝操作]:若某组拼接不成立,且此时 已拼接的长度为0 或 当前已拼接的长度与刚才枚举的长度之和为最终枚举的答案时,则可直接跳出循环.因为此时继续 ...
- 洛谷P1120 小木棍
洛谷1120 小木棍 题目描述 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50. 现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长 ...
- 【题解】洛谷P1120 小木棍(搜索+剪枝+卡常)
洛谷P1120:https://www.luogu.org/problemnew/show/P1120 思路 明显是搜索题嘛 但是这数据增强不是一星半点呐 我们需要N多的剪枝 PS:需要先删去超出50 ...
- 洛谷 P1120 小木棍 [数据加强版]解题报告
P1120 小木棍 [数据加强版] 题目描述 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50. 现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它 ...
- 洛谷——P1120 小木棍 [数据加强版]
P1120 小木棍 [数据加强版] 题目描述 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过5050. 现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍 ...
- 洛谷 P1120 小木棍 [数据加强版]
P1120 小木棍 [数据加强版] 题目描述 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50. 现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它 ...
- 洛谷P1120 小木棍 [搜索]
题目传送门 题目描述乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50. 现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度. 给出每段小木棍 ...
- 洛谷 P1120 小木棍 dfs+剪枝
Problem Description [题目链接] https://www.luogu.com.cn/problem/P1120 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不 ...
- 洛谷P1120小木棍[DFS]
题目描述 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50. 现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度. 给出每段小木棍的长度,编 ...
- [洛谷P1120]小木棍 [数据加强版]
题目大意:有一些同样长的木棍,被切割成几段(长$\leqslant$50).给出每段小木棍的长度,找出原始木棍的最小可能长度. 题解:dfs C++ Code: #include<cstdio& ...
随机推荐
- hdu 5852 :Intersection is not allowed! 行列式
有K个棋子在一个大小为N×N的棋盘.一开始,它们都在棋盘的顶端,它们起始的位置是 (1,a1),(1,a2),...,(1,ak) ,它们的目的地是 (n,b1),(n,b2),...,(n,bk). ...
- android studio 代码混淆如何忽略第三方jar包
日前在打包混淆包含第三方jar包的Android studio项目时 报出了各种错误,但是debug版本却能正常运行,于是怀疑android studio 打包的时候把第三方jar包给混淆了,第三方j ...
- web项目中配置文件的加载顺序
当一个项目启动时,首先是web.xml: 这里面的配置: 为什么要在web.xml中配置struts的过滤器? 因为一个web项目运行的时需要加载的,或者默认的部分配置都会在web.xml中配置,中间 ...
- python学习(24) 使用Xpath解析并抓取美女图片
Xpath最初用来处理XML解析,同样适用于HTML文档处理.相比正则表达式更方便一些 Xpath基本规则 nodename 表示选取nodename 节点的所有子节点 / 表示当前节点的直接子节点 ...
- linux命令总结之netstat命令
简介 Netstat 命令用于显示各种网络相关信息,如网络连接,路由表,接口状态 (Interface Statistics),masquerade 连接,多播成员 (Multicast Member ...
- K8S从私有仓库拉取镜像
通常来讲,我们在通过公共镜像仓库拉取docker镜像的时候,不需要任何的认证操作,但我们在构建了企业的私有镜像以后,就不得不在拉取镜像之前通过用户名密码来完成认证. 在docker单机环境中,我们可以 ...
- 总结: 《jQuery基础教程》 5-完结
第5章:操作DOM HTML属性和DOM属性:attr()和prop() 获取表单控件的值:val() DOM操作方法的归纳: (1) 要在HTML中创建新元素,使用$()函数.(2) 要在每个匹配的 ...
- 面试心得与总结---BAT、网易、蘑菇街
作者:Xoper.ducky链接:https://www.nowcoder.com/discuss/3043来源:牛客网 之前实习的时候就想着写一篇面经,后来忙就给忘了,现在找完工作了,也是该静下心总 ...
- openstack中的server
一.HTTP server 主要是horizon模块,horizon是基于Python Django搭建的web应用,其运行于Apache网络服务器上(当然也可以运行在其他web服务器上),主要功能就 ...
- Java并发编程原理与实战七:线程带来的风险
在并发中有两种方式,一是多进程,二是多线程,但是线程相比进程花销更小且能共享资源.但使用多线程同时会带来相应的风险,本文将展开讨论. 一.引言 多线程将会带来几个问题: 1.安全性问题 线程安全性可能 ...