计算机算法分析课程设计 - 1. 动态规划—最优二叉搜索树2. 回溯法—图的着色

更新时间:2023-09-02 21:43:01 阅读量: 教育文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

成 绩 评 定 表

课程设计任务书

摘 要

算法设计与分析,其实可以解释为一类优化问题,一般针对可以利用计算机解决的离散型问题的优化。主要目的就是为了解决某一问题而提出的各种不同的解决方案,并且要针对具体问题做细致的空间与时间复杂度分析。本文通过计算机算法分析设计出解最优二叉搜索树问题的动态规划算法和设计出解图的着色问题全部可行解的回溯法算法,利用C++语言编写程序实现算法。

动态规划算法是将待求解的问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。首先找出最优解的性质,并刻画其结构特征,然后递归的定义最优值(写出动态规划方程)并且以自底向上的方式计算出最优值,最后根据计算最优值时得到的信息,构造一个最优解。 回溯法算法是确定了解空间的组织结构后,回溯法就是从开始节点(根结点)出发,以深度优先的方式搜索整个解空间。这个开始节点就成为一个活结点,同时也成为当前的扩展结点。在当前的扩展结点处,搜索向纵深方向移至一个新结点。这个新结点就成为一个新的或节点,并成为当前扩展结点。如果在当前的扩展结点处不能再向纵深方向移动,则当前的扩展结点就成为死结点。换句话说,这个节点,这个结点不再是一个活结点。此时,应往回(回溯)移动至最近一个活结点处,并使这个活结点成为当前的扩展结点。回溯法即以这种工作方式递归的在解空间中搜索,直到找到所要求的解或解空间中以无活结点为止。即通过确定初始解和剪枝函数原则画出状态图进行搜索产生全部可行解。

关键词:动态规划;二叉搜索树;回溯法;剪枝原则;C++

目 录

一、课程设计目的.................................................... 1 二、课程设计内容.................................................... 1 三、概要设计........................................................ 1

3.1 动态规划—最优二叉搜索树.................................... 1 3.2 回溯法—图的着色............................................ 1 四、详细设计与实现.................................................. 2

4.1 动态规划—最优二叉搜索树.................................... 2

4.1.1最优二叉搜索树问题描述和分析 .......................... 2 4.1.2最优子结构性质 ........................................ 3 4.1.3递归计算最优值 ........................................ 4 4.1.4算法实现题 ............................................ 4 4.2 回溯法—图的着色............................................ 6

4.2.1 图的m着色问题描述.................................... 6 4.2.2 算法设计.............................................. 7 4.2.3算法实现题 ............................................ 8

总 结............................................................. 13 参考文献........................................................... 14

一、课程设计目的

《计算机算法设计与分析》这门课程是一门实践性非常强的课程,要求我们能够将所学的算法应用到实际中,灵活解决实际问题。通过这次课程设计,能够培养我们独立思考、综合分析与动手的能力,并能加深对课堂所学理论和概念的理解,可以训练我们算法设计的思维和培养算法的分析能力。

二、课程设计内容

1、动态规划:设计出解最优二叉搜索树问题的动态规划算法; 2、回溯法:设计出解图的着色问题全部可行解的回溯法算法。

三、概要设计

3.1 动态规划—最优二叉搜索树

动态规划的基本思想是将问题分解为若干个小问题,解子问题,然后从子问题得到原问题的解。设计动态规划法的步骤: (1)找出最优解的性质,并刻画其结构特征; (2)递归地定义最优值(写出动态规划方程); (3)以自底向上的方式计算出最优值;

(4)根据计算最优值时得到的信息,构造一个最优解。 3.2 回溯法—图的着色

回溯法的基本思想是确定了解空间的组织结构后,回溯法就是从开始节点(根结点)出发,以深度优先的方式搜索整个解空间。这个开始节点就成为一个活结点,同时也成为当前的扩展结点。在当前的扩展结点处,搜索向纵深方向移至一个新结点。这个新结点就成为一个新的或节点,并成为当前扩展结点。如果在当前的扩展结点处不能再向纵深方向移动,则当前的扩展结点就成为死结点。

换句话说,这个节点,这个结点不再是一个活结点。此时,应往回(回溯)移动至最近一个活结点处,并使这个活结点成为当前的扩展结点。回溯法即以这种工作方式递归的在解空间中搜索,直到找到所要求的解或解空间中以无活结点为止。

用回溯法解决图的着色问题的步骤:

(1)针对所给问题,定义问题的解空间; (2)确定易于搜索的解空间结构;

(3)以深度优先方式搜索解空间,并在搜索过程中用剪枝函数原则避免无效搜索。

四、详细设计与实现

4.1 动态规划—最优二叉搜索树 4.1.1最优二叉搜索树问题描述和分析

设S x1,x2, ,xn 是有序集,且x1 x2 xn,表示有序集S的二叉搜索树利用二叉树的结点存储有序集中的元素。它具有下述性质:存储于每个结点中的元素x大于其左子树中任一结点所存储的元素,小于其右子树中任一结点所存储的元素。二叉树的叶结点是形如 xi,xi 1 的开区间,在表示S的二叉搜索树中搜索元素x,返回的结果有两种情况:

(1)在二叉搜索树的内结点中找到x xi。

(2)在二叉搜索树的叶结点中确定x xi,xi 1 。

设在第(1)中情形中找到元素x xi的概率为bi;在第(2)种情形中确定

x xi,xi 1 的概率为ai。其中约定x0 ,xn 1 。显然有:

ai 0,0 i n;bj 0,1 j n; ai bj 1

i 0

j 1

nn

a0,b1,a1, ,bn,an 称为集合S的存取概率分布。

在表示S的二叉搜索树T中,设存储元素xi的结点深度为ci;叶结点 xj,xj 1

的结点深度为dj,则:

p bi 1 ci ajdj

i 1

j 0

nn

表示在二叉搜索树T中进行一次搜索所需要的平均比较次数,p又成为二叉搜索树T的平均路长。在一般情况下,不同的二叉搜索树的平均路长是不相同的。

最优二叉搜索树问题是对于有序集S及其存取概率分布 a0,b1,a1, ,bn,an ,在所有表示有序集S的二叉搜索树中找到一棵具有最小平均路长的二叉搜索树。 4.1.2最优子结构性质

二叉搜索树T的一棵含有结点xi, ,xj和叶结点 xi 1,xi , , xj,xj 1 的子树可以看作是有序集 xi, ,xj 关于全集合 xi 1, ,xj 1 的一棵二叉搜索树,其存取概率为以下的条件概率:

k bk/wij i k j h ah/wij i 1 h j

公式中:wij ai 1 bi bj aj,1 i j n。

设Tij是有序集 xi, ,xj 关于存取概率i 1,i, ,j,j的一棵最优二叉搜索树,其平均路长为pij。Tij的根结点存储元素xm。其左右子树Tl和Tr的平均路长分别为pl和pr。由于Tl和Tr中结点深度是它们在Tij中的结点深度减1,故有:

wi,jpi,j wi,j wi,m 1pl wm 1,jpr

由于Tl是关于集合 xi, ,xm 1 的一棵二叉搜索树,故pl pi,m 1。若

pl pi,m 1,则用Ti,m 1替换Tl可得到平均路长比Tij更小的二叉搜索树。这与Tij是最优二叉搜索树矛盾。故Tl是一棵最优二叉搜索树。同理可证Tr也是一棵最优二叉搜索树。因此最优二叉搜索树问题具有最优子结构性质。

4.1.3递归计算最优值

最优二叉搜索树Tij的平均路长为pij,则所求的最优值为p1,n。由最优二叉搜索树问题的最优子结构性质可建立计算pij的递归式如下:

wi,jpi,j wi,j min wi,k 1pi,k 1 wk 1,jpk 1,j ,i j

i k j

初始时,pi,i 1 0,1 i n。

记wi,jpi,j为m i,j ,则m 1,n w1,np1,n p1,n为所求的最优值。 计算m i,j 的递归式为:

m i,j wi,j min m i,k 1 m k 1,j ,i j

i k j

m i,i 1 0,1 i n

据此,可设计出解最优二叉搜索树问题的动态规划算法。 4.1.4算法实现题

给出标识符集{1,2,3}={do,if,stop}存取概率,若b1=0.4 b2=0.2 b3=0.05 a0=0.2 a1=0.05 a2=0.05 a3=0.05构造一棵最优二叉搜索树 源程序如下:

#include<iostream>

using namespace std;

void OptimalBinarySearchTree(float a[],float b[],int n,float m[][20],int s[][20],float w[][20])

{ //求解最优值的方法 int i,r,k; float t;

for(i=0;i<=n;i++){

w[i+1][i]=a[i]; //搜索不到的点,最优解为0 m[i+1][i]=0; }

for(r=0;r<n;r++)

for(i=1;i<=n-r;i++){

int j=i+r; //左子树为空 w[i][j]=w[i][j-1]+a[j]+b[j]; m[i][j]=m[i+1][j];

s[i][j]=i;

for(k=i+1;k<=j;k++){

t=m[i][k-1]+m[k+1][j]; if(t<m[i][j])

{ //以k为根节点,左子树不为空 m[i][j]=t;

s[i][j]=k; } }

m[i][j]+=w[i][j]; for(i=1;i<=n;i++)

for(int j=1;j<=n;j++)

cout<<"s["<<i<<"]["<<j<<"]="<<s[i][j]<<endl; }

void print(int i,int j,int s[][20],int S[]) //递归输出结果 {

if(j>=i){ int k=s[i][j]; cout<<"("; print(i,k-1,s,S); cout<<")";

cout<<" "<<S[k]<<" "; cout<<"(";

print(k+1,j,s,S); cout<<")"; } }

int main()

{ //主函数 int n,i;

float a[20],b[20],m[20][20],w[20][20]; int s[20][20],S[20];

cout<<"请输入有序集元素的个数n:"<<endl; cin>>n;

cout<<"请输入有序集各元素的值S[i](一共"<<n<<"个):"<<endl; for(i=1;i<=n;i++) cin>>S[i];

cout<<"请输入概率数组a的各元素的值a[i](一共"<<n+1<<"个):"<<endl; for(i=0;i<=n;i++) cin>>a[i];

cout<<"请输入概率数组b的各元素的值b[i](一共"<<n<<"个):"<<endl; for(i=1;i<=n;i++) cin>>b[i];

OptimalBinarySearchTree(a,b,n,m,s,w);

}

cout<<"最优值即平均步长为:"<<m[1][n]<<endl; return 0;

}

运行结果如下:

图1 运行结果

4.2 回溯法—图的着色 4.2.1 图的m着色问题描述

给定无向连通图G和m种不同的颜色。用这些颜色为图G的各顶点着色,每个顶点着一种颜色。是否有一种着色法使G中每条边的2个顶点着不同颜色。这个问题是图的m可着色判定问题。若一个图最少需要m种颜色才能使图中每条边连接的2个顶点着不同颜色,则称这个数m为该图的色数。求一个图的色数m的问题称为图的m可着色优化问题。

四色问题是m图着色问题的一个特例,根据四色原理,证明平面或球面上的任何地图的所有区域都至多可用四种、颜色来着色,并使任何两个有一段公共边界的相邻区域没有相同的颜色。这个问题可转换成对一平面图的4-着色判定问题(平面图是一个能画于平面上而边无任何交叉的图)。将地图的每个区域变成一个结点,若两个区域相邻,则相应的结点用一条边连接起来。多年来,虽然已证明用5种颜色足以对任一幅地图着色,但是一直找不到一定要求多于4种颜色

的地图。直到1976年这个问题才由爱普尔,黑肯和考西利用电子计算机的帮助得以解决。他们证明了4种颜色足以对任何地图着色。

图2 四色问题

4.2.2 算法设计

首先把所有顶点的颜色初始化为0,然后依次为每个顶点着色。如果其中i个顶点已经着色,并且相邻两个顶点的颜色都不一样,就称当前的着色是有效的局部着色;否则,就称为无效的着色。如果由根节点到当前节点路径上的着色,对应于一个有效着色,并且路径的长度小于n,那么相应的着色是有效的局部着色。这时,就从当前节点出发,继续探索它的儿子节点,并把儿子结点标记为当前结点。在另一方面,如果在相应路径上搜索不到有效的着色,就把当前结点标记为死结点,并把控制转移去搜索对应于另一种颜色的兄弟结点。如果对所有m个兄弟结点,都搜索不到一种有效的着色,就回溯到它的父亲结点,并把父亲结点标记为死结点,转移去搜索父亲结点的兄弟结点。这种搜索过程一直进行,直到根结点变为死结点,或者搜索路径长度等于n,并找到了一个有效的着色为止。

一般连通图的可着色法问题并不仅限于平面图。给定图G V,E 和m种颜色,如果这个图不是m可着色,则给出否定答案;如果这个图是m可着色的,找出所有不同的着色方法。

下面根据回朔法的递归描述框架Backtrack设计图的m着色算法。用图的邻接矩阵a表示无向量连通图G V,E 。若 i,j 属于图G V,E 的边集E,则

a i j 1,否则a i j 0。整数1,2,…,m用来表示m种不同颜色。顶点i所

有颜色用x i 表示,数组x 1:n 是问题的解向量。问题的解空间可表示为一棵高度为n+1的完全m叉树。解空间树的第i 1 i n 层中每一结点都有m个儿子,每个儿子相应于x i 的m个可能的着色之一。 第n+1层结点均为叶结点。

在下面的解图的m可着色问题的回溯法中,Backtrack i 搜索解空间中第i层

子树。类Color的数据成员记录解空间中结点信息,以减少传给Backtrack的参数。sum记录当前已找到的m着色方案数。

在算法Backtrack中,当i n时,算法搜索至叶结点,得到新的m着色方案,当前找到的m着色方案数sum则增1。而当i n时,当前扩展结点Z的每一个解空间中内部结点.该结点有x i 1,2, ,m共m个儿子结点.对当前扩展结点Z的每一儿子结点,有方法ok检查其可行性,并以深度优先的方式递归的对可行子树搜索,或减去不可行树。

复杂度分析:图m可着色问题的解空间树中内结点个数是O(mn),对于每一个内结点,在最坏情况下,用ok检查当前扩展结点的每一个儿子所相应的颜色可用性需耗时O(nmn)。因此,回溯法总的时间耗费是: n﹣ 1 i= 0

i

n

n

∑ m ( mn) ) = nm( m — 1) /( m —1 = O ( nm

)

4.2.3算法实现题

给定如图3所示的一个无向连通图G,现有4种不同的颜色,用这4种颜色为图G的各顶点着色,每个顶点着一种颜色。要求:G中每条边的2个顶点着有不同的颜色。问一共有多少种着色方案?

图2

利用回溯法给上图2着色。通过确定初始解和剪枝函数原则(G中每条边的2个顶点着有不同的颜色)进行搜索产生可行解,搜索过程如图3 状态树所示。

其具体步骤如下:

1. 把5元组初始化为(0,0,0,0,0),从根结点开始向下搜索,以颜色1为顶点A着色,生成根结点2时,产生(1,0,0,0,0)是个有效着色,向下搜索。 2. 以颜色1为顶点B着色生成结点3时,产生(1,1,0,0,0)是个无效着色,结点3为死结点。以颜色2为顶点B着色生成结点4,产生(1,2,0,0,0)是个有效着色,向下搜索。

3. 分别以颜色1和2为顶点C着色生成结点5和6,产生(1,2,1,0,0)和(1,2,2,0,0)都是无效着色,因此结点5和6都是死结点。以颜色3为顶点C着色生成结点7,产生(1,2,3,0,0)是个有效着色,向下搜索。

4. 以颜色1为顶点D着色生成结点8,产生(1,2,3,1,0)是个有效着色,向下搜索。

5. 分别以颜色1、2、3和4为顶点E着色生成结点9、10、11、12,产生(1,2,3,1,1)和(1,2,3,1,2)都是无效着色,因此结点9和10都是死结点;产生(1,2,3,1,3)和(1、2、3、1、4)都是有效着色。返回结点7向下搜索。 6. 分别以颜色2和3为顶点D着色生成结点13和14,产生(1,2,3,2,0)和(1,2,3,3,0)都是无效着色,因此结点13和14都是死结点。以颜色4为顶点D着色生成结点15,产生(1,2,3,4,0)是个有效着色,向下搜索,最后得到有效着色(1,2,3,4,1)和(1、2、3、4、3)。

7. 重复上述步骤,得到全部着色方案。

沈阳理工大学

源程序如下:

#include <iostream> using namespace std;

int n; //图的顶点个数 int m; //可用颜色数 int i,j;

int a[10][10]; //程序中使用时从下标1开始;程序中用于存储图的邻接矩阵 int x[10]; //用于存储当前解

long sum; //当前已找到的可着色方案数 bool Ok(int k) {

for(int j=1;j<=n;j++) { if((a[k][j]==1)&&(x[j]==x[k])) //a[k][j]==1表示的是第k点和第j //点是相连的 return false; }

return true; }

void Backtrack(int t) {

if(t>n) //t是表示的第t行叶结点;图的m着色共 //有n个结点 { sum++; cout<<" 第"<<sum<<"种解决方案为 :\n"; for(int i=1;i<=n;i++) { cout<<x[i]<<" "; } cout<<endl; } else { for(int i=1;i<=m;i++) { x[t]=i; if(Ok(t)) {

Backtrack(t+1); //判断t+1结点的颜色是不是正确 } x[t]=0; //把t+1结点的颜色换一种 } } }

long mColoring(int mm) {

m=mm; sum=0;

Backtrack(1); return sum; }

void main() {

cout<<"\n\t==========图的m着色问题============\n"; cout<<"输入图的顶点数与可用的颜色数 :\n"; cin>>n>>m;

cout<<"\n==========输入图的邻接矩阵\n"; for(i=1;i<=n;i++) for(j=1;j<=n;j++) cin>>a[i][j];

cout<<"\n==========判断可着色性\n"; mColoring(m); if(sum==0)

cout<<" 无可行方案!"<<endl;

cout<<"-------------------------------------------------------------------------------"<<endl; cout<<n<<" 个顶点"<<"按所给的邻接关系着 "<<m<<" 种颜色,总的着色方案有 "<<sum<<" 个\n"; }

运行结果如下:

图4

图5

总 结

通过本次课程设计,使我对快速排序、最优二叉搜索树以及图的m着色设计

的基本过程的设计方法、步骤、思路、有了一定的了解与认识。在这次课程设计过程中,我认识到只是知道课本上的理论知识是远远不够的,我们还必须要深切的理解每个算法的思想,并且能够利用c++语言去编写相关的代码,经过不断的修改、调试,使之能解决相应的问题,最终能运用到实际案例中去。 对我们来说,实际能力的培养至关重要,而这种实际能力的培养单靠课堂教学是远远不够的,必须从课堂走向实践。而这次的课程设计,正好给了我们一个机会让我们找出自身状况与实际需要的差距,让我的编程思路变得更开阔了,拥有了更严格的设计与分析算法的思维方式,改变了我随意拼凑算法的习惯,并在以后的学习期间及时补充相关知识,为求职与正式工作做好充分的知识、能力准备,从而缩短从校园走向社会的心理转型期。

参考文献

[1]王晓东. 计算机算法设计与分析(第4版). 北京.电子工业出版社. 2012.2 [2]王晓云、陈业纲. 计算机算法设计、分析与实现. 北京.科学出版社. 2012 [3]谭浩强. C语言程序设计(第3版). 北京.清华大学出版社. 2012 [4]陈玉福. 算法设计与分析 . 北京.清华大学出版社. 2009 [5]严蔚敏. 数据结构 . 北京.清华大学出版社. 2009

本文来源:https://www.bwwdw.com/article/a28i.html

Top