一本通例题埃及分数—题解&&深搜的剪枝技巧总结
一、简述:
众所周知,深搜(深度优先搜索)的时间复杂度在不加任何优化的情况下是非常慢的,一般都是指数级别的时间复杂度,在题目严格的时间限制下难以通过。所以大多数搜索算法都需要优化。形象地看,搜索的优化过程就像将搜索树上没用的枝条剪下来,因此搜索的优化过程又叫剪枝。剪枝的实质就是通过判断决定是否要沿当前枝条走下去。
二、搜索的剪枝必需遵循三个原则:
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> ...
随机推荐
- python+selenium切换窗口(获取句柄信息)
current_window_handle:获得当前窗口句柄: window_handles:返回所有窗口的句柄到当前会话: switch_to.window(suoyou[0]) ========= ...
- base64 换表 解密脚本
做逆向经常遇到换表的base64 有了py脚本 一切都好说: import base64 import string str1 = "x2dtJEOmyjacxDemx2eczT5cVS9f ...
- Zend Framework MVC的结构
The Zend Framework MVC Architecture 一.概述: In this chapter, we will cover the following topics:1. Zen ...
- c++多线程并发学习笔记(2)
等待一个时间或其他条件 在一个线程等待完成任务时,会有很多选择: 1. 它可以持续的检查共享数据标志(用于做保护工作的互斥量),直到另一个线程完成工作时对这个标志进行重设.缺点:资源浪费,开销大 2. ...
- Web服务器——WSGI
1.什么是WSGI? WSGI全称 Web Server Gateway Interface,也可称作Python Web Server Gateway Interface,开始于2003年,为Pyt ...
- P4290 [HAOI2008]玩具取名
传送门 $dp$ 设 $f[i][j][k]$ 表示初始为 $k$ 时,能否得到 $[i,j]$ 这一段子串 设 $pd[i][j][k]$ 表示长度为二的字符串 $ij$ 能否由 $k$ 得到 然后 ...
- PHP通过exec执行git pull
目标 项目没有使用Jenkins部署,使用的是Gitlab+ECS,要实现代码的自动部署 想法 使用Gitlab的钩子,当某个分支的代码提交之后,访问一个URL,实现代码的自动部署.这里使用PHP的e ...
- struts2_对Map进行双层迭代
转自:struts2_对Map进行双层迭代 //后台数据 public String execute() throws Exception { Map<String, List<Produ ...
- SpringAOP用到了什么代理,以及动态代理与静态代理的区别
spring aop (面向切面)常用于数据库事务中,使用了2种代理. jdk动态代理:对实现了接口的类生成代理对象.要使用jdk动态代理,要求类必须要实现接口. cglib代理:对类生成代理对象. ...
- APP元素定位工具weditor
github地址https://github.com/openatx/weditor python -m weditor --shortcut adb devices 在页面上输入手机设备号,点Con ...