第四章搜索法
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
(3)有的枚举问题的状态是复合型的,由多个子状态组成。 这时可以考虑将原问题化为更小的问题。表面上看是一 个量的变化,但如果充分利用问题的信息,可能使算祛 产生质的变化。 (4)根据问题的性质进行截肢,特别是最优化枚举问题, 往往可以根据问题性质抖除一些“显然”不可能的状态, 从而达到减少状态总数的效果。 (5)引进其他算法。一个可“直译”成枚举算法的问题,其 解题思路可以从枚举开始,并不一定非得以枚举结束, 很多枚举问题可以通过引进其他高效算法达到解题目的。
#include <iostream> #include <cmath> using namespace std; int main() { int len[20]; //存放三角形的边长 存放三角形的边长 double x[20]; //存放三角形的顶点 坐标 存放三角形的顶点x坐标 存放三角形的顶点 double y[20]; //存放三角形的顶点 坐标 存放三角形的顶点y坐标 存放三角形的顶点 //输入三角形的个数和最大水平初速度 输入三角形的个数和最大水平初速度 int n; double v0; cin>>n>>v0; for(int i=0;i<n;i++) cin>>len[i];
因此: 当然,这个v不一定符合要求,它应满足两个条件 (1)它不能大于极限速度v0,即必须有v<=v0 ; (2)跳跃过程中不得碰到其他三角形。
条件2的判断要复杂一些。好在题目中提示:轨迹是一条 上凸的曲线,因此只要顶点在抛物线下方,则整条线段 都在抛物线下方。这样,我们依次判断起点和终点之间 的各个三角形顶点k,看它是否在抛物线下方。 如何判断顶点k是否在抛物线下方呢?我们可以算出到达 时间t0=dx/v (其中dx为起点到顶点k的水平坐标增量), 然后算出该时刻的垂直坐标增量,vt0-5t02 。如果此 增量大于起点到顶点k的垂直坐标增量,则抛物线在上 方。只需起点和终点之间任何一个三角形的顶点不在抛 物线下方,则跳远不能完成。我们在枚举过程中不断将 小孩所能跳到的点j调整为best。枚举结束后,best 即 为试题要求的最远点。
【例1 】跳远
在水平面上整齐地放着n个正三角形, 在水平面上整齐地放着n个正三角形,相邻两个三角 形的底边之间无空隙,如图所示。 形的底边之间无空隙,如图所示。
一个小孩想站在某个三角形i的顶端,跳到三角形j 一个小孩想站在某个三角形i的顶端,跳到三角形j的顶端 上(i<j)。他总是朝着斜向45度的方向起跳,且初始 i<j)。他总是朝着斜向45度的方向起跳, )。他总是朝着斜向45度的方向起跳 水平速度v不超过一个给定值v0。 水平速度v不超过一个给定值v0。 v0
信息数字化,表面上看是非数值的问题,但经过数字化处 理后,就可以方便地进行算法设计了。 警察抓小偷。警察局抓了a,b,c,d四名偷窃嫌疑犯, a,b,c,d四名偷窃嫌疑犯 【例2】警察抓小偷。警察局抓了a,b,c,d四名偷窃嫌疑犯, 其中只有一人是小偷,审问结果如下: 其中只有一人是小偷,审问结果如下: a说:“我不是小偷。” 我不是小偷。 b说:“c是小偷。” 是小偷。 c说:“小偷肯定是d。” 小偷肯定是d d说:“c是在冤枉人。” 是在冤枉人。 现在已知4个人3人说的是真话,一人说的是假话, 现在已知4个人3人说的是真话,一人说的是假话,问到底 谁是小偷? 谁是小偷?
分析:本题的基本思想是枚举。对于每一个起跳点i,依 次判断点i+1,i+2,…,n能否跳到。 状态:起跳点i和i点后的点j ,每个状态元素的取值范围: 1<=i<=n-1, i+1<=j<=n 约束条件的分析:判断小孩能否从i点跳到j点的方法如下: 设起点和终点间的水平距离为l、垂直距离为h,则由物 理知识有:
第一节 枚举法
枚举法的基本思想是根据提出的问题枚举所有可能 状态,并用问题给定的条件检验哪些是需要的,哪些是 不需要的。能使命题成立,即为其解。虽然枚举法本质 上属于搜索策略,但是它与后面讲的回溯法有所不同, 因为适用枚举法求解的问题必须满足两个条件:
(1)可预先确定每个状态的元素个数 n ; (2)状态元素 a1, a2,……,an的可能值为一个连续的值 域。 设:ai1 ― 状态元素 ai的最小值; aik一状态元素ai的最大值(1<=i<=n), 即 a11<=a1<=a1k; ai1<=ai<=aik, …… a21<=a2<=a2k; an1<=an<=ank
第四章、搜索法
下面介绍3种最常用的搜索策略 (1)枚举法 (2)回溯法 (3)分支限界法(广度优先搜索)
无论什么类型的试题,只要能归纳出数学模型,就 尽量用解析方法求解。因为一个好的数学模型建立了 客观事物间准确的运算关系,运用这个数学模型直接 求解是再合适不过的了。当然,这仅是一种可能性, 因为并非所有选手都能在竞赛的“一瞬间”把问题分 析得如此透彻,并非所有给定的问题都能建立数学模 型,即便有了数学模型,解该模型的准确方法也不一 定能套用现成算法。因此在某些情况下,还需要通过 搜索(列举所有可能情况)来寻求问题的解。
在跳跃过程中,由于受到重力作用(忽略空气阻力), 小孩将沿着抛物线行进, 水平运动方程为 x=x0+vt , 竖直运动方程为 y=y0+vt-0.5gt2, 运动轨迹是一条上凸的抛物线。取g=10.0, (x0,y0)是起 跳点坐标。 请编程求出他从每个位置起跳能到达的最远三角形的编号。 注意:跳跃过程中不许碰到非起点和终点的其他三角形。
//计算每个三角形的顶点坐标 计算每个三角形的顶点坐标 x[0]=len[0]/2; y[0]=len[0]*sqrt(3)/2; for(i=1;i<n;i++) { x[i]=x[i-1]+len[i-1]/2+len[i]/2; y[i]=len[i]*sqrt(3)/2; } //依次计算每个三角形所能达到的最远点 //依次计算每个三角形所能达到的最远点。 依次计算每个三角形所能达到的最远点。 for(i=1;i<n;i++) { int best=0; //所能达到的最右的三角 所能达到的最右的三角 形的编号 cout<<"from "<<i<<" ";
#include <iostream> using namespace std; void main() { for(char x='a';x<='d';x++) if((x!='a')+(x=='c')+(x=='d')+(x!='d')==3) cout<<x<<" is a thief."<<endl; }
-般来说,如果确定了枚举变量的个数及其 值域,我们则可以利用 for 语句和条件判 断语句逐步求解或证明: for(a1=a11;a1<=a1k;a1++) for(a2=a21;a2<=a2k;a2++) …… for(ai=ai1;ai<=aik;ai++) …… for(an=an1;an<=ank;an++) if 状态(a1,a2… ai,…,an)满足检验条件 输出问题的解; 由此可见,枚举的次数为
问题分析:将’a’,’b’,’c’,’d’对四个人进行编号。则问题 可用枚举法来解决。 算法设计:用变量x存放小偷的编号,则x的取值范围’a’ 取到’d’,就假设了他们中的某人是小偷的所有情况。 4个人所说的话就可以分别写成: a说的话:x!=‘a’或!(x==‘a’) b说的话:x==‘c’ c说的话:x==‘d’ d说的话:x!=‘d’或!(x==‘d’) 已知4个人3人说的是真话, 已知4个人3人说的是真话,一人说的是假话可以表示为 当这4个逻辑式的值相加等于3 当这4个逻辑式的值相加等于3。
一、“直译”的枚举算法 直译”
将自然语言描述的问题“翻译”成算法的过程, 即为“直译”。现实生活中的许多问题可以“直译”成 枚举算法。 例如古代著名的“百鸡百钱”(公鸡一只5文钱,母 鸡一只3文钱,小鸡3只一文钱。要求计算一百钱可买3 种鸡一百只的数量。)问题就是一个典型的枚举问题。
状态: 3 种鸡的数量 x , y , z 每个状态元素的取值范围:
for(int j=i+1;j<=n;j++) {double l=x[j]-x[i]; double h=y[j]-y[i]; if (l<h) break; //起跳角为 度 起跳角为45度 起跳角为 double v=sqrt(5*l*l/(l-h)); if (v>v0) break; //大于最大初速度 大于最大初速度 bool ok=true; //判断是否碰到其他三角形 判断是否碰到其他三角形 for(int k=i+1;k<j;k++) { double t=(x[k]-x[i])/v; if ((v*t-5*t*t)-(y[k]-y[i])<0.0) {ok=false; break;} }if (ok) best=j; else break; } cout<<best<<endl; }return 0; }
约束条件: 5x +3y+ z /3 = 100 由此“直译”成如下枚举算法
for(int x=1; x<18;x++) for(int y=1;y<31;y++) { int z=100-x-y; if(5*x+3*y+z/3.0==0) cout<<x<<y<<z<<endl; }
从上例可以看出,能够被“直译”的问题一般具有下 列特征: (1)建立在离散模型上; (2)状态的数量和枚举单个状态的代价确定; (3)枚举算法的时间复杂度为一个多项式。
枚举法的优点: (1)由于枚举算法一般是现实生活中问题的“直译”,因 此比较直观,易于理解; (2)由于枚举算法建立在考察大量状态、甚至是穷举所有 状态的基础上,所以算法的正确性比较容易证明。 枚举法的缺点:枚举算法的效率取决于枚举状态的 数量及单个状态枚举的代价,因此效率比较低。 当然,由于模型建立的不同,信息提取量的不同, 同一个问题可以有多个枚举算法,效率也可能有所不同, 甚至可能有很大的差异。
我们称状态元素为枚举变量。例如某问题的枚举变量有 3个: a1 , a2 , a3 。其中: 1<=a1<=2, 2<=a2<=4, 5<=a3<=7 则问题的可能状态集为: (a1,a2,a3)={(1,2,5),(1,2,6),(1,2,7), (1,3,5),(1,3,6),(1,3,7), (1,4,5),(1,4,6),(1,4,7), (2,2,5),(2,2,6),(2,2,7), (2,3,5),(2,3,6),(2,3,7), (2,4,5),(2,4,6),(2,4,7)} 在上述 18 个可能的状态集中,满足问题给定的检验条件 的状态即为问题的解。
输入: 第1行为两个正整数n,v0 (3<=n<=20,1<=v0<= 100) ,表示 三角形的个数和最大水平初速度。 第2行有n个正整数li(1<=li<=20),表示从左到右各个三 角形的边长。 输出: 输出仅一行,包括n-1个数,表示从三角形1,2,3,…,n-1 的顶点出发能到达的最右边的三角形编号。如果从某三 角形出发无法达到任何三角形,相应的数为0 。
二、枚举算法的优化
枚举算法的时间复杂度可以用状态总数乘以考 察单个状态的耗时来表示,因此优化主要是: 减少状态总数(即减少枚举变量和枚举变量的 值域); 降低单个状态的考察代价。
优化过程从几个方面考虑。具体为: (1)提取有效信息,即在枚举问题给出的浩瀚信息中,取 其对解题有直接帮助的精华,弃其无用的糟粕。这样做, 会对减少状态总数有很大作用。 (2)减少重复计算。虽然从表面上看,各个状态是独立的, 但是不同状态在考察过程中可能有一部分相同的内容。 如果让互相联系的状态共同“分担”考ห้องสมุดไป่ตู้费用,可以降 低时间复杂度。
例
3位老师对某次数学竞赛进行了预测,他们的预测结
果如下: 甲说:学生A得第一名,学生B得第三名。 乙说:学生C得第一名,学生D得第四名。 丙说:学生D得第二名,学生A得第三名。 竞赛结果表明,他们都说对了一半,说错了一半,并且无 并列名次,试求出A、B、C、D各自的名次。 问题分析:用数1,2,3,4分别代表学生A、B、C、D获得 的名次,问题就可以利用三重循环把所有的情况枚举出 来。