一本通例题埃及分数—题解&&深搜的剪枝技巧总结
一、简述:
众所周知,深搜(深度优先搜索)的时间复杂度在不加任何优化的情况下是非常慢的,一般都是指数级别的时间复杂度,在题目严格的时间限制下难以通过。所以大多数搜索算法都需要优化。形象地看,搜索的优化过程就像将搜索树上没用的枝条剪下来,因此搜索的优化过程又叫剪枝。剪枝的实质就是通过判断决定是否要沿当前枝条走下去。
二、搜索的剪枝必需遵循三个原则:
1、正确性(不能把正解排除,要不然搜什么呢?)2、准确性(尽可能把不能通向正解的枝条剪去)3、高效性(因为在每个枝条上都要进行一次判断,如果判断的复杂度很高,也会相应地给总复杂度“加料”)
三、剪枝的常用入手点(上面说的都是套话,下面可是干货了)
1、搜索顺序(例如有些题要求不能重复且解中最大值在所有解的最大值中最小的最优解,可以按照从小到大设计搜索,否则其他顺序不仅搜索效率低,代码相应也会很繁琐);
解释一下为什么书中说不一样的搜索顺序,相应的搜索树的形态结构也不一样:当不加任何剪枝时,最直观的体现就是树从根节点开始分支不一样多。因为最终的可行解是一样多的(先不考虑最优性排除的非最优解),而只有搜索树搜到了叶节点才会搜到可行解。如果搜索深度也一样,那么两个搜索顺序对应的搜索树即为两个深度、叶节点树相同,但节点分支的递增顺序不同(可能是一个数较浅地方的分支较小,另一个反之...)。进行剪枝后,情况就不一样了:剪枝时,若剪去的枝条规模越大,那么剪枝也就越成功。那么,在一定的深度剪枝时,对更深的地方分支多的那颗搜索树剪枝的效果就要比对另一颗更深的地方分支少的搜索树剪枝的效果好。因此在剪枝判断很难继续优化的前提下,搜索顺序也会很大程度地影响剪枝的效果。
2、搜索对象:不一样的搜索对象,一般相应的复杂度也不一样。
3、可行性剪枝:当搜索到一个节点时,发现已经不能通向解,就立即回溯。更强一步:对以后的节点,只走能通向解的节点。
*上下界剪枝:有些题仔细分析会发现一个节点的分支是有一个区间,要靠我们分析出区间的上下界,叫上下界剪枝(可行性剪枝中非常常用的一类剪枝,以至于都有自己的名字了(滑稽))。
4、最优性剪枝:当搜索到一个节点时,发现当前状态已经比已得到的最优解差,这是无论向下走的策略再怎么好,也不能比已知解更优,所以立即回溯。更强一步:发现当前状态在未来不会优于已知最优解回溯。
5、排除等效冗杂/记忆化:当发现当前状态已经搜过(记忆化),或当前状态的等效状态已经搜过,就不往下搜了。
小总结:搜索时面临的状态其实都有多个“维度”,或是说多个方面(长度,体积,位置,顺序,密度,增长率至整体的每个基础的组成元素等等),这些维度常常出现在题目描述、给出的变量、要求求的解中。仔细阅读,找到这些维度,结合题目的约束条件或这些维度自己本身的性质,思考剪枝的入手点,根据可行性、最优性或界限列出有关当前花费的不等式,即为初步剪枝。初步剪枝不足以满足AK的可以再考虑列出有关未来花费的不等式(有时用到预处理),还可以再结合各维度之间的关系(例如圆半径与面积、位置与重力势能的关系等等,能懂就好)列出判断用不等式。这不就是推导不等式嘛。
看看例题吧:
条件少得可怜,抛过来一个问题就让我们做,显然是一道搜索题。但是我们不知道到底要用多少个加数,也不知道加数的分母要枚举到多大,这就是这道题的难点。
由于决定最优解的第一条件是加数的个数,所以我们可以先枚举加数的个数limit,尝试limit个单位分数将a/b分解(迭代加深搜索)。为了避免重复搜索,假设第一个单位分数>第二个单位份数>第三个单位分数>....;即第一个分母<第二个分母<第三个分母...这样我们确定的搜索顺序——个数从小到大,对于每个个数,分母从小到大。
设当前的分母为xk,a/b还剩rest没有分解完,因为1/xk+1/xk+1+..+1/xlimit=rest,1/xk>1/xk+1>...>1/xlimit,所以1/xk>rest/(limit-k+1),即xk<(limit-k+1)/limit。因为1/xk<rest,所以xk>1/rest。因为分母按从小到大的顺序搜索,所以xk有大于xk-1。这样就进行了一波强大的上下界剪枝。
当xk>=已知解的最大的分母maxans时,再进行下去也没有意义了(因为无论如何解都不会被这条路径更新了),所以xk<maxans。又进行了一波最优性剪枝。
TIP:对于分数的存储,最好不要算出值来存,因为浮点数总是有误差的,虽然一开始的误差很小,但在在大量的运算下,很容易就把误差放得很大,很大,很大(重要的事情说三遍),所以最好用分子分母存储法。但也注意分子分母可能也很大,很大,很大(重要的事情说三遍),因此别忘了数据类型开大点,能约分就约分(约分有时还能直接撞到最优解)。
AC代码:
- #include<iostream>
- #include<cstdio>
- #include<cmath>
- #include<algorithm>
- #include<cstring>
- using namespace std;
- int bj,len;
- long long ans[],an[],maxans=0x7fffffffffffff;
- long long gcd(long long a,long long b)
- {
- return a%b?gcd(b,a%b):b;
- }
- int ok(int k)//判断是否更优
- {
- if(!len)
- {
- len=k;
- bj=;
- maxans=an[k];
- return ;
- }
- if(an[k]<maxans)
- {
- maxans=an[k];
- return ;
- }
- return ;
- }
- void dfs(int limit,int lst,int k,long long zi,long long mu)
- {
- if(k==limit)
- {
- if(zi==&&mu>an[k-])
- {
- an[k]=mu;
- if(ok(k)) memcpy(ans,an,sizeof(long long) * (k+));
- }
- return;
- }
- long long li=(limit-k+)*mu/zi-((limit-k+)*mu%zi==);
- long long a1,b1;
- for(int i=max((long long)lst+,mu/zi+);i<=li&&i<maxans;i++)//上下界剪枝+最优性剪枝
- {
- an[k]=i;
- a1=mu*i;//模拟分数通分再加减的过程
- b1=zi*i-mu;
- long long g=gcd(b1,a1);
- dfs(limit,i,k+,b1/g,a1/g);
- }
- }
- int main()
- {
- // freopen("fraction.in","r",stdin);//调试用
- // freopen("fraction.out","w",stdout);
- long long a,b;
- cin>>a>>b;
- long long g=gcd(a,b);
- a/=g;b/=g;
- if(a==)
- {
- cout<<b;
- return ;
- }
- for(int i=;;i++)//枚举加数个数
- {
- dfs(i,,,a,b);
- if(bj) break;
- }
- for(int i=;i<=len;i++)
- printf("%lld ",ans[i]);
- return ;
- }
一本通例题埃及分数—题解&&深搜的剪枝技巧总结的更多相关文章
- 深搜的剪枝技巧(三)——Sticks(可行性剪枝、上下界剪枝、最优性剪枝)
小木棍(最优性剪枝.可行性剪枝) 一.问题描述 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,已知每段的长都不超过 50 .现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍 ...
- 【笔记】「pj复习」深搜——简单剪枝
深搜--简单剪枝 说在最前面: 因为马上要 NOIP2020 了,所以菜鸡开始了复习qwq. pj 组 T1 ,T2 肯定要拿到满分的,然后 T3 , T4 拿部分分, T3 拿部分分最常见的做法就是 ...
- 深搜+DP剪枝 codevs 1047 邮票面值设计
codevs 1047 邮票面值设计 1999年NOIP全国联赛提高组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题目描述 Description ...
- 【深搜加剪枝】【HDU1455】【Sticks】
题目大意:有一堆木棍 由几个相同长的木棍截出来的,求那几个相同长的木棍最短能有多短? 深搜+剪枝 具体看代码 #include <cstdio> #include <cstdlib& ...
- hdu 1010 Tempter of the Bone(深搜+奇偶剪枝)
Tempter of the Bone Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Othe ...
- LOJ P10022 埃及分数 题解
每日一题 day62 打卡 Analysis 这道题一看感觉很像搜索,但是每次枚举x∈(1,10000000)作为分母显然太蠢了. 所以我们要想办法优化代码. 优化一:迭代加深 优化二: 我们确定了搜 ...
- HDOJ/HDU Tempter of the Bone(深搜+奇偶性剪枝)
Problem Description The doggie found a bone in an ancient maze, which fascinated him a lot. However, ...
- POJ-1190-生日蛋糕(深搜,剪枝)
生日蛋糕 Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 23049 Accepted: 8215 Description 7月1 ...
- [Vijos1308]埃及分数(迭代加深搜索 + 剪枝)
传送门 迭代加深搜索是必须的,先枚举加数个数 然后搜索分母 这里有一个强大的剪枝,就是确定分母的范围 #include <cstdio> #include <cstring> ...
随机推荐
- 【Linux开发】Ubuntu图形界面切换与磁盘扩展分区
Ubuntu14.04设置字符界面快捷键:ctrl-alt-f1 切换回图形界面:ctrl-alt-f7 为虚拟机拓展了30G的空间,挂在了/mnt/sda3这个目录下: 说明一下Ubuntu14.0 ...
- .net core 学习小结之 自定义JWT授权
自定义token的验证类 using System; using System.Collections.Generic; using System.IO; using System.Linq; usi ...
- [Git] 023 Re:从零开始的 rebase 命令
1. 开门见山 我新建了一个本地仓库,并进行了一些操作 当前情况 查看(直观但不明了) 上图的第二条 "log" 命令详见 [Git] 024 log 命令的补充 的 " ...
- 极*Java速成教程 - (4)
Java语言基础 多态 多态是面向对象的一大重要特性,如果说封装是隐藏一个类怎么做,继承是确定一系列的类做什么,那多态就是通过手段去分离做什么和怎么做. 向上转型与收窄 在开发者将一类事物封装成类以后 ...
- bootstrap使用总结(carousel设置大小。item设置大小,img设置大小)
在bootstrap中使用carousel,先要给.carousel一个大小, 要想使carousel和item和img随着浏览器大小而变,就要设置 .carousel .item { height: ...
- 目标检测(三) Fast R-CNN
引言 之前学习了 R-CNN 和 SPPNet,这里做一下回顾和补充. 问题 R-CNN 需要对输入进行resize变换,在对大量 ROI 进行特征提取时,需要进行卷积计算,而且由于 ROI 存在重复 ...
- 思维体操: HDU1022Train Problem I
Train Problem I Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) ...
- 分布式事务解决方案汇总:2PC、3PC、消息中间件、TCC、状态机+重试+幂等(转)
数据一致性问题非常多样,下面举一些常见例子.比如在更新数据的时候,先更新了数据库,后更新了缓存,一旦缓存更新失败,此时数据库和缓存数据会不一致.反过来,如果先更新缓存,再更新数据库,一旦缓存更新成功, ...
- 使用Docker部署Spring-Boot+Vue博客系统
在今年年初的时候,完成了自己的个Fame博客系统的实现,当时也做了一篇博文Spring-boot+Vue = Fame 写blog的一次小结作为记录和介绍.从完成实现到现在,也断断续续的根据实际的使用 ...
- Find The Multiple (水题)
Given a positive integer n, write a program to find out a nonzero multiple m of n whose decimal repr ...