}
}
}
算法复杂度分析:
算法matrixChain的主要计算量取决于算法中对r,i和k的3重循环。
循环体内的计算量为O
(1),而3重循环的总次数为O(n3)。
因此算法的计算时间上界为O(n3)。
算法所占用的空间显然为O(n2)。
2.3贪心算法
顾名思义,贪心算法总是作出在当前看来最好的选择。
也就是说贪心算法并不从整体最优考虑,它所作出的选择只是在某种意义上的局部最优选择。
当然,希望贪心算法得到的最终结果也是整体最优的。
虽然贪心算法不能对所有问题都得到整体最优解,但对许多问题它能产生整体最优解。
如单源最短路经问题,最小生成树问题等。
在一些情况下,即使贪心算法不能得到整体最优解,其最终结果却是最优解的很好近似。
可用贪心算法解决的问题的性质:
1)贪心选择性质
2)最优子结构性质
举例:
最优装载问题
有一批集装箱要装上一艘载重量为c的轮船。
其中集装箱i的重量为Wi。
最优装载问题要求确定在装载体积不受限制的情况下,将尽可能多的集装箱装上轮船。
算法描述
最优装载问题可用贪心算法求解。
采用重量最轻者先装的贪心选择策略,可产生最优装载问题的最优解。
具体算法描述如下。
template
voidLoading(intx[],Typew[],Typec,intn)
{
int*t=newint[n+1];
Sort(w,t,n);
for(inti=1;i<=n;i++)x[i]=0;
for(inti=1;i<=n&&w[t[i]]<=c;i++){x[t[i]]=1;c-=w[t[i]];}
}
最优装载问题满足贪心算法的两个基本性质,可以用贪心算法实现。
2.4回溯法
回溯法的基本做法是搜索,或是一种组织得井井有条的,能避免不必要搜索的穷举式搜索法。
这种方法适用于解一些组合数相当大的问题。
回溯法在问题的解空间树中,按深度优先策略,从根结点出发搜索解空间树。
算法搜索至解空间树的任意一点时,先判断该结点是否包含问题的解。
如果肯定不包含,则跳过对该结点为根的子树的搜索,逐层向其祖先结点回溯;否则,进入该子树,继续按深度优先策略搜索。
为了避免生成那些不可能产生最佳解的问题状态,要不断地利用限界函数(boundingfunction)来处死那些实际上不可能产生所需解的活结点,以减少问题的计算量。
具有限界函数的深度优先生成法称为回溯法。
回溯法的基本思想:
(1)针对所给问题,定义问题的解空间;
(2)确定易于搜索的解空间结构;
(3)以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索
举例分析:
符号三角形问题:
---+
-+++-
-++-
-+-
--
+
下图是由14个“+”和14个“-”组成的符号三角形。
2个同号下面都是“+”,2是“-”。
解法:
解向量:
用n元组x[1:
n]表示符号三角形的第一行。
可行性约束函数:
当前符号三角形所包含的“+”个数与“-”个数均不超过n*(n+1)/4
无解的判断:
n*(n+1)/2为奇数
voidTriangle:
:
Backtrack(intt)
{
if((count>half)||(t*(t-1)/2-count>half))return;
if(t>n)sum++;
else
for(inti=0;i<2;i++){
p[1][t]=i;
count+=i;
for(intj=2;j<=t;j++){
p[j][t-j+1]=p[j-1][t-j+1]^p[j-1][t-j+2];
count+=p[j][t-j+1];
}
Backtrack(t+1);
for(intj=2;j<=t;j++)
count-=p[j][t-j+1];
count-=i;
}
}
复杂度分析
计算可行性约束需要O(n)时间,在最坏情况下有O(2n)个结点需要计算可行性约束,故解符号三角形问题的回溯算法所需的计算时间为O(n2n)。
2.5分支限界法
分支限界法常以广度优先或以最小耗费(最大效益)优先的方式搜索问题的解空间树。
在分支限界法中,每一个活结点只有一次机会成为扩展结点。
活结点一旦成为扩展结点,就一次性产生其所有儿子结点。
在这些儿子结点中,导致不可行解或导致非最优解的儿子结点被舍弃,其余儿子结点被加入活结点表中。
此后,从活结点表中取下一结点成为当前扩展结点,并重复上述结点扩展过程。
这个过程一直持续到找到所需的解或活结点表为空时为止。
常见的两种分支限界法:
(1)队列式(FIFO)分支限界法
按照队列先进先出(FIFO)原则选取下一个节点为扩展节点。
(2)优先队列式分支限界法
按照优先队列中规定的优先级选取优先级最高的节点成为当前扩展节点。
举例:
0-1背包问题
算法思想:
首先,要对输入数据进行预处理,将各物品依其单位重量价值从大到小进行排列。
在下面描述的优先队列分支限界法中,节点的优先级由已装袋的物品价值加上剩下的最大单位重量价值的物品装满剩余容量的价值和。
算法首先检查当前扩展结点的左儿子结点的可行性。
如果该左儿子结点是可行结点,则将它加入到子集树和活结点优先队列中。
当前扩展结点的右儿子结点一定是可行结点,仅当右儿子结点满足上界约束时才将它加入子集树和活结点优先队列。
当扩展到叶节点时为问题的最优值。
部分算法如下:
while(i!
=n+1){//非叶结点
//检查当前扩展结点的左儿子结点
Typewwt=cw+w[i];
if(wt<=c){//左儿子结点为可行结点
if(cp+p[i]>bestp)bestp=cp+p[i];
AddLiveNode(up,cp+p[i],cw+w[i],true,i+1);}
up=Bound(i+1);
//检查当前扩展结点的右儿子结点
if(up>=bestp)//右子树可能含最优解
AddLiveNode(up,cp,cw,false,i+1);
//取下一个扩展节点(略)
}
3结合0-1背包问题详述动态规划、贪心算法、回溯法、分支限界法解决问题的过程
0-1背包问题:
给定n种物品和一背包。
物品i的重量是wi,其价值为vi,背包的容量为C。
问应如何选择装入背包的物品,使得装入背包中物品的总价值最大?
动态规划算法:
设所给0-1背包问题的子问题
的最优值为m(i,j),即m(i,j)是背包容量为j,可选择物品为i,i+1,…,n时0-1背包问题的最优值。
由0-1背包问题的最优子结构性质,可以建立计算m(i,j)的递归式如下。
算法复杂度分析:
从m(i,j)的递归式容易看出,算法需要O(nc)计算时间。
当背包容量c很大时,算法需要的计算时间较多。
例如,当c>2n时,算法需要Ω(n2n)计算时间。
改进算法:
由m(i,j)的递归式容易证明,在一般情况下,对每一个确定的i(1≤i≤n),函数m(i,j)是关于变量j的阶梯状单调不减函数。
跳跃点是这一类函数的描述特征。
在一般情况下,函数m(i,j)由其全部跳跃点唯一确定。
对每一个确定的i(1≤i≤n),用一个表p[i]存储函数m(i,j)的全部跳跃点。
表p[i]可依计算m(i,j)的递归式递归地由表p[i+1]计算,初始时p[n+1]={(0,0)}。
函数m(i,j)是由函数m(i+1,j)与函数m(i+1,j-wi)+vi作max运算得到的。
因此,函数m(i,j)的全部跳跃点包含于函数m(i+1,j)的跳跃点集p[i+1]与函数m(i+1,j-wi)+vi的跳跃点集q[i+1]的并集中。
易知,(s,t)q[i+1]当且仅当wisc且(s-wi,t-vi)p[i+1]。
因此,容易由p[i+1]确定跳跃点集q[i+1]如下q[i+1]=p[i+1](wi,vi)={(j+wi,m(i,j)+vi)|(j,m(i,j))p[i+1]}
另一方面,设(a,b)和(c,d)是p[i+1]q[i+1]中的2个跳跃点,则当ca且d
除受控跳跃点外,p[i+1]q[i+1]中的其它跳跃点均为p[i]中的跳跃点。
由此可见,在递归地由表p[i+1]计算表p[i]时,可先由p[i+1]计算出q[i+1],然后合并表p[i+1]和表q[i+1],并清除其中的受控跳跃点得到表p[i]。
改进后复杂性分析:
上述算法的主要计算量在于计算跳跃点集p[i](1≤i≤n)。
由于q[i+1]=p[i+1](wi,vi),故计算q[i+1]需要O(|p[i+1]|)计算时间。
合并p[i+1]和q[i+1]并清除受控跳跃点也需要O(|p[i+1]|)计算时间。
从跳跃点集p[i]的定义可以看出,p[i]中的跳跃点相应于xi,…,xn的0/1赋值。
因此,p[i]中跳跃点个数不超过2n-i+1。
由此可见,算法计算跳跃点集p[i]所花费的计算时间为
从而,改进后算法的计算时间复杂性为O(2n)。
当所给物品的重量wi(1≤i≤n)是整数时,|p[i]|≤c+1,(1≤i≤n)。
在这种情况下,改进后算法的计算时间复杂性为O(min{nc,2n})。
贪心算法:
在选择装入背包的物品时,对每种物品i只有2种选择,即装入背包或不装入背包。
不能将物品i装入背包多次,也不能只装入部分的物品i。
贪心算法的两条性质,可以放入物品的部分,使它适合背包问题。
不适合0-1背包问题,所以不能用贪心算法计算。
回溯法:
回溯法的三个条件:
解空间:
子集树
可行性约束函数:
上界函数:
template
TypepKnap:
:
Bound(inti)
{//计算上界
Typewcleft=c-cw;//剩余容量
Typepb=cp;
//以物品单位重量价值递减序装入物品
while(i<=n&&w[i]<=cleft){
cleft-=w[i];
b+=p[i];
i++;
}
//装满背包
if(i<=n)b+=p[i]/w[i]*cleft;
returnb;
}
分支界限法:
如上第二部分2.5所述。
4对比分析以上四种算法策略
四种算法中都用到了最优子结构这一概念,判断用何种算法,取决于具体问题的具体分析,看是否适用本身,能达到最优算法。
动态规划算法与分治算法相似。
用于贪心算法的有活动安排问题,最优装载问题,哈夫曼编码问题,单源最短路径问题。
对于回溯法,通过约束找到满足条件的所有解,特点为能进就进,不能进就退回来,与递归类似。
分支法与回溯法类似,但解的目标是通过约束找到满足条件的一个解,或找到在某种意义下的最优解。
回溯法以深度优先的方式搜索解空间树,而分支限界法则以广度优先或以最小耗费优先的方式搜索解空间树。
5课程总结
通过本课程的学习,加强了我的思维能力,增强了思考问题的深度,也了解到,在编写程序时,算法对于一个程序的重要性,以前只知道程序就是代码,现在知道,代码并不是最重要的部分,好的算法可以说是一个程序成功的关键,也是功能好坏的体现,好的算法可以提高程序的效率,在一定程度上可以说是没有算法,就谈不上编写程序,通俗的说,算法在是一种解决问题的方法,是从事计算机行业人员的必备能力。
参考文献
[1]王晓东.计算机算法设计与分析.电子工业出版社
[2]余祥宣,崔国华,邹海明.计算机算法基础[M].武汉:
华中科技大学出版社,2003.
[3]吕国英,任瑞征,钱宇华.算法设计与分析:
清华大学出版社.
[4]《算法设计与分析》(第二版)霍红卫编著,西安电子科技大学出版社.