每日一题 day54 打卡

Analysis

一,管理员已经在题目中告诉你输入时去掉长度大于50的木棍。

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

  其长度至少是最长的一根木棍,此时最长的这根木棍恰好单独组成原来的长棍。如果 原始长度 小于 最长的这根木棍,那么这根最长的木棍就无法自己或与其它木棍组成原来的长棍。

  其长度至多是所有木棍的长度之和,此时所有的木棍拼在一起恰好成为一根原来的长棍。如果 原始长度 大于所有木棍的长度之和,那么即使所有木棍拼在一起也组不够原来的长棍了。

  这么大的循环套dfs会超时么?当然会了。所以我们可以考虑到当 原始长度 不能被 所有木棍的长度之和 整除的话,这些木棍是拼不出整数根的(如果都拼成枚举的原来长棍的长度)。因此在循环时把它们刷掉。

  这里借鉴了dalao的(小)优化,即原始长度枚举到 所有木棍的长度之和/2 即可,因为此时所有木棍有可能拼成2根木棍,原始长度再大的话就只能是所有木棍拼成1根了。

三,脑补一下怎么搜。设dfs(int now_num,int before_num,int rest_length),now_num表示正在拼第几根原来的长棍,before_num表示使用的上一根木棍(输入的短棍)的编号,rest_length表示当前在拼的长棍还有多少长度未拼。于是循环枚举下一根将要使用的木棍。

四,你开始思考对程序做一些优化。(下面的优化请按顺序想)

  1. 一根长木棍肯定比几根短木棍拼成同样长度的用处小,即短的木棍可以更灵活组合,所以对输入的所有木棍按长度从大到小排序,从长到短地将木棍拼入,这样短木棍可以更加灵活地接在。

 如果你还不太清楚“灵活”的含义,请形象脑补一下——如果先用短木棍,那么需要很多根连续的短木棍接上一根长木棍才能拼成一根原来的长棍,那么短木棍都用了后,剩下了大量长木棍,拼起来就不如短木棍灵活,更难接出原始长度。而先用长木棍,最后再用短木棍补刀,这样就剩下了相对较短的木棍,能更加灵活地拼接出原始长度。

  1. 根据优化1,将输入的木棍从大到小排好序后,当用木棍i拼合原始长棍时,从第i+1根木棍开始往后搜。

  2. 当dfs返回拼接失败,需要更换当前使用的木棍时,不要再用与当前木棍的长度相同的木棍,因为当前木棍用了不行,改成与它相同长度的木棍一样不行。这里我预处理出了排序后每根木棍后面的最后一根与这根木棍长度相等的木棍(程序中的next数组),它的下一根木棍就是第一根长度不相等的木棍了。

 这个预处理可以优化时间,不必在循环中慢慢往下找长度不相等的木棍。

  1. 只找木棍长度不大于未拼长度rest的所有木棍。我看其他大部分人的做法(包括书上的啊)都是直接在循环中判断,但我认为可以根据木棍长度的单调性来二分找出第一个木棍长度不大于未拼长度rest。它后面的木棍一定都满足这个条件。

  2. 用vis数组标记每根木棍是否用过。另外在dfs回溯的时候别忘了去掉这些标记,这样就不用每次dfs之前memset了(memset用多的话速度可TM慢了)!

 优化5的习惯可以沿用到各种竞赛

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

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

 当前长棍剩余的未拼长度等于当前木棍的长度时,这根木棍在最优情况下显然是拼到这(如果用更多短木根拼完剩下的这段,把这根木棍留到后面显然不如把更多总长相等的短木棍扔到后面)。如果在最优情况下继续拼下去失败了,那肯定是之前的木棍用错了,回溯改即可。

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

 #include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define int long long
#define maxn 65
#define rep(i,s,e) for(register int i=s;i<=e;++i)
#define dwn(i,s,e) for(register int i=s;i>=e;--i)
using namespace std;
inline int read()
{
int x=,f=;
char c=getchar();
while(c<''||c>'') {if(c=='-') f=-; c=getchar();}
while(c>=''&&c<='') {x=x*+c-''; c=getchar();}
return f*x;
}
inline void write(int x)
{
if(x<) {putchar('-'); x=-x;}
if(x>) write(x/);
putchar(x%+'');
}
int n,m,cnt,sum,flag,len;
int a[maxn],nxt[maxn],book[maxn];
bool cmp(int x,int y){return x>y;}
void dfs(int now_num,int before_num,int rest_length)
{
if(rest_length==)
{
if(now_num==m) {flag=; return;}
int num=;
rep(i,,cnt)
{
if(book[i]==) continue;
num=i;
break;
}
book[num]=;
dfs(now_num+,num,len-a[num]);
book[num]=;
if(flag==) return;
}
int l=before_num+,r=cnt;
while(l<r)
{
int mid=(l+r)>>;
if(a[mid]<=rest_length) r=mid;
else l=mid+;
}
rep(i,l,cnt)
{
if(book[i]==) continue;
book[i]=;
dfs(now_num,i,rest_length-a[i]);
book[i]=;
if(flag==) return;
if(rest_length==len||rest_length==a[i]) return;
i=nxt[i];
if(i==cnt) return;
}
}
signed main()
{
n=read();
rep(i,,n)
{
int x=read();
if(x>) continue;
a[++cnt]=x;
sum+=x;
}
sort(a+,a+cnt+,cmp);
nxt[cnt]=cnt;
dwn(i,cnt-,)
{
if(a[i]==a[i+]) nxt[i]=nxt[i+];
else nxt[i]=i;
}
for(len=a[];len<=sum;++len)
{
if(sum%len!=) continue;
m=sum/len;
flag=;
book[]=;
dfs(,,len-a[]);
book[]=;
if(flag==) {write(len); return ;}
}
write(sum);
return ;
}

请各位大佬斧正(反正我不认识斧正是什么意思)

洛谷 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的木棍过滤掉真的坑了不少人(包括我). 显然是一道DFS题 .考虑剪枝. 找找搜索要面临的维度.状态:原始木棍 ...

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

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

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

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

随机推荐

  1. 从nsurlsession、Alamofire到moya

    更好的理解(抽象).更少的构建(配置).更方便的表达(语言) 一.iOS系统的网络编程(DSL概念) ios缺省的网络编程只是给出了网络编程的基本概念: urlsession.request.resp ...

  2. Linux学习笔记之RAID笔记

    RAID: Redundant Arrays of Inexpensive Disks Independent Berkeley: A case for Redundent Arrays of Ine ...

  3. 关于visual studio 2015 智能提示英文,而非中文的解决方案

    关于visual studio 2015 智能提示英文,而非中文的解决方案:   找到这个目录 C:\Program Files (x86)\Reference Assemblies\Microsof ...

  4. 拷贝 vs 赋值

    其实我只要关注两个动作就够了: 拷贝.赋值=            (而构造.析构的步骤都是没疑问的.) ——>   赋值=  都是显式调用的, 而拷贝构造可以显示调,也可能隐式被调. 下图错误 ...

  5. [Tools] 多媒体视频处理工具FFmpeg

    FFMpeg FFmpeg是一套可以用来记录.转换数字音频.视频,并能将其转化为流的开源计算机程序.采用LGPL或GPL许可证.它提供了录制.转换以及流化音视频的完整解决方案.它包含了非常先进的音频/ ...

  6. English--动词时态

    English|动词时态 时态是一个很玄乎的东西,要么是完全掌握,要么是不知所云. 在正式开始之前,大家需要明白汉语的谓语动词是不会随着时间与状态而变化.但是,英语的谓语动词会随着时间与状态发生变化. ...

  7. 为什么K8s会成为主流?

    容器技术和K8s是云原生概念的核心和基础.云计算诞生已有超过10年,但云计算时代的应用到底该是什么样子,一直没人能说清楚,也没人能确定云计算的基础架构将会如何发展.在K8s出现之前,没人设想过会有一个 ...

  8. 【转载】C#通过Rows.Count属性获取总行数

    在C#中的Datatable数据变量的操作过程中,有时候我们需要知道DataTable中是否含有数据行或者DataTable的数据总行数,此时我们就可以先拿到DataTable中的Rows属性对象,通 ...

  9. Java 面向对象—杂项(方法不能重写,修饰符,变量)

    一.哪些方法不能被重写? 1.final 修饰的不能重写 2.static 修饰的不能重写 3.private 修饰的,因为私有的在子类中不可见 4.如果跨包的话,修饰符缺省的也不能被重写,因为缺省的 ...

  10. 12 ARM汇编

    Android系统采用java作为平台软件基础开发语言,NDK使Android平台可以运行C/C++代码这些代码汇编成ARM的elf可执行文件. 原生程序生成过程 经历4步:1.预处理2.编译3.汇编 ...