第一章 算法与数据结构

1.2 基本算法

在进行算法分析的时候,要着重考虑算法的时间复杂度,选择最优算法

1.2.1 枚举算法

枚举算法核心思想:在很多时候,无法立刻得出某个问题的可行解或者最优解,但是可以用一种比较“笨”的方法通过列举所有情况然后逐一判断得到的结果。
缺点:浪费时间、有可能漏掉某些情况
作用:枚举是一种理想的辅助算法,在毫无头绪的情况下,枚举为我们打开一个缺口,让其他算法得以成功。
练习
离散函数——不会
有一个离散函数,定义在集合{1,2,3…,N},取值在-232…232。请找出函数图像上两个点,使得函数在这两点之间的点都在两点连线的下方,且此连线的斜率尽量大。N≤10000.(注:时间复杂度为O(n2)的算法容易想到,那么O(n)算法呢?)

1.2.2 贪心法

每次选择当前最优策略

概述:每次选择一个局部最优策略进行实施,而不去考虑对今后的影响。一般来说,它的时间复杂度比较低,算法实现页比较容易,但是很多题目贪心法并不能得到最优解。即使可以,也比较难证明解的最优性。
例子

  • 最优装载问题
    单一元素
  • 部分背包问题
    多种元素单一限制
  • 乘船问题
    多种元素多种限制
  • 区间覆盖问题

两个调度问题
流水作业调度
有n个作业要在两台机器M1和M2组成的流水线上完成加工。每个作业i都必须先花时间ai在M1上加工,然后花时间bi在M2上加工。确定n个作业的加工顺序,使得从作业1在机器M1上加工开始到作业n在机器M2上加工为止总时间最少。
Johnson算法
设N1为a < b的作业集合,N2 为a ≥ b的作业集合,将N1中的作业按照a非减序排序,N2中的作业按照b非增序排序,则N1作业接N2作业构成最优顺序。
显然算法的核心只是一个排序过程,时间复杂度为O(nlogn),关键在于它的正确性证明。
带限期和罚款的单位时间任务调度
有n个单位时间任务,每个任务需要单位长度时间。第i个任务的限期是di,超时罚款为fi,给n个任务排序使得所有未按时完成的任务罚款总和尽量小。
矩阵胚(matroid)
给定非空有限集S,I是S的一类具有遗传性质的独立子集族(即空集属于I,且对于I里的任意元素B,B的任意子集也属于I)。我们把I中的元素称为独立子集。如果I满足交换性质(即对于I的任意两个元素A和B,若|A| < |B|,一定可以找到一个元素x ∈ B − A,使得A ∪ {x}仍然是独立集),称有序对(S,I)为矩阵胚,或称拟阵。
对于独立集A,如果存在x使得A∪{x}仍是独立集,称x为A的可扩展元素。没有可扩展元素的独立集A称为极大独立集
定理1:矩阵胚的所有极大独立集具有相同的基数。
最大权独立子集问题 给S的每个元素x赋予正权W(x) > 0,独立集的权被定义为其中所有元素的权和。求加权矩阵胚中权最大的独立子集。
贪心算法
从空集开始(权为0),每次选择权最大的可扩展元素加入独立集中,直到不存在可扩展元素为止,此时得到的独立集一定是权最大独立子集。
应用
应用一:Huffman编码

练习
喷水装置——不懂
有一块草坪,长为l,宽为w,在它的中心线上不同位置处装有n(n≤10000)个点状的喷水装置。每个喷水装置i的喷水效果会让以它为中心半径为ri的圆被润湿。请选择尽量少的喷水装置,把整个草坪全部润湿。

1.2.3 递归与分治

分治是递归的典型应用

递归法概述:递归法把问题转化为规模更小的子问题解决。递归法思路清晰,编程简单,但有时候难以想到。如果确定了用递归法解题,思考的重点应该放到建立原问题和子问题之间的联系。有的问题有很明显的递归结构,但是需要仔细思考,才能正确的转化为结构相同的子问题。
分治法使用递归的思考方式,遵守以下方法:
划分(divide):把问题划分为若干子问题
求解(conquer):递归求解子问题
合并(combine):把子问题的解合并成原问题的解
其中第二个步骤需要递归调用,而划分子问题和合并子问题的解需要仔细设计算法。
注意:尽量减少递归的调用,尤其不要进行重复的计算
典型事例

  • 棋盘覆盖问题
  • 循环日程表问题
  • 巨人与鬼

三个经典的分治算法

  • 最大值与最小值
  • 有序表查找问题
  • 快速幂

1.2.4 递推法

有一类试题,每相邻两项数之间的变化有一定的规律性,我们可将这种规律归纳成如下简捷的递推关系式: F n = g ( F n − 1 ) F_n=g(F_{n-1}) Fn=g(Fn1)
所谓倒推法,就是在不知初始值的情况下,经某种递推关系而获知问题的解或目标,再倒过来,推知它的初始条件,因为这类问题的运算过程是一一映射的,故可分析得其递推公式。然后再从这个解或目标出发,采用倒推手段,一步步地倒推到这个问题的初始陈述。
一般的分析思路:

if 求解初始条件 F~1~
	then begin{ 倒推 }
	由题意(或递推关系)确定最终结果F~n~;
	求出倒推关系式F~i-1~=g'(F~i~);
		i=n;{从最终结果F~n~出发进行倒推}
		while 当前结果F~i~非初始值F~1~ do由F~i-1~=g(F~i~)倒推前项;
		输出倒推结果F~1~,和倒推过程;
		end{then}
else begin{顺推}
	由题意(或递推关系)确定初始值F~1~(边界条件);
	求出顺推关系式F~i~=g(F~i-1~);
	i=1;{由边界条件F~1~,出发进行顺推}
	whie 当前结果F~i~,非最终结果F~n~ do由F~i~=g(F~i-1~)顺推后项;
	输出顺推结果F~n~,和顺推过程;
	end;{else}  

1.2.5 动态规划 dynamic programming

动态规划的思想源于递归,是一种问题转化策略。
Fibonacci sequence(合并状态)

基本思想:建立子问题的描述,建立状态间的转移关系,使用递推或记忆化搜索法来实。
动态规划要点
1、状态定义:用问题的某些特征参数描述一个子问题
2、状态转移方程:即状态值之间的递推关系。这个方程通常需要考虑两个部分:一是递推的顺序;而是递推的边界(也是递推的起点)。
注意:**重叠子问题(overlapping subproblem)**是动态规划展示威力的关键。


一般的计算方法

将问题转化为一个个子问题,根据总结出的固有表达式,实现子问题(subproblem)的解决思路相同。
前提:问题解决的思路,总结出表达式
方法一:递归计算
缺点:时间效率太低,原因为重复计算
方法二:递推计算
即从最小的问题开始计算,往前递推,直到顶部。
优点:减少重复计算
方法三:记忆化搜索
使用一个visited数组作为当前结点是否被访问的记录,被访问后直接调用结果。
根据题目情况设置数组初始化的值

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值