洛谷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& ...
随机推荐
- Apache Commons IO之FileUtils的常用方法
Apache Commons IO 在学习io流的时候研究(翻译)了一下这个,只有FileUtils的某些方法,并不全面,还请谅解 org.apache.commons.io 这个包下定义了基于 st ...
- POJ 1966 Cable TV Network 【经典最小割问题】
Description n个点的无向图,问最少删掉几个点,使得图不连通 n<=50 m也许可以到完全图? Solution 最少,割点,不连通,可以想到最小割. 发现,图不连通,必然存在两个点不 ...
- E. Mahmoud and Ehab and the function Codeforces Round #435 (Div. 2)
http://codeforces.com/contest/862/problem/E 二分答案 一个数与数组中的哪个数最接近: 先对数组中的数排序,然后lower_bound #include &l ...
- golang json 编码解码
json 编码 package main import ( "encoding/json" "fmt" ) type Person struct { Name ...
- Java入门:注册模块的实现
1.主活动图 用户选择注册选项,进入注册界面,开始输入注册信息,到最后完成注册.具体的活动图如下: 以上活动图中,矩形框里的操作不是在一个类里面实现的,而是通过Form类和UserService类来实 ...
- P2627 修剪草坪
P2627 修剪草坪 题目描述 在一年前赢得了小镇的最佳草坪比赛后,Farm John变得很懒,再也没有修剪过草坪.现在,新一轮的最佳草坪比赛又开始了,Farm John希望能够再次夺冠. 然而,Fa ...
- Java基础-面向对象第二特征之继承(Inheritance)
Java基础-面向对象第二特征之继承(Inheritance) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.继承的概述 在现实生活中,继承一般指的是子女继承父辈的财产.在程序 ...
- 编译的java工程压缩上传到linux服务器上后,中文的类名显示乱码
首先声明,类名是用中文命名的,这个别人写的,不允许修改. 本地用7zip软件压缩成zip包,传到服务器解压,发现中文的class文件名称是乱码. 解决办法: 方法一:使用jar命令打成jar包,传到服 ...
- java 修饰符总结
java中的修饰符分为类修饰符,字段修饰符,方法修饰符.根据功能的不同,主要分为以下几种. 1.权限访问修饰符 public,protected,default,private,这四种级别 ...
- Spyder简述
导言 想打造轮子, 就必须要有一套完善的造轮子的工具. 我在jupyter+sciTE的组合里转来转去, 最后还是打算放弃这个组合, 因为离开了自动完成/调用提示/随时随地的访问文档帮助, 前行之路太 ...