跟着《算法导论》学习——动态规划法(Dynamic programming)

读前声明:本人所写帖子主要为了记录本人学习的一个过程,无他想法,由于内容比较肤浅,如有雷同,非常正常!!!

本文内容:

本文主要是参考《算法导论》这本书,完成部分算法编写,可能编程习惯或者风格比较差,还请多多批评。

  和分治法一样,动态规划是通过组合子问题的解而解决整个问题的。与分治法不同的是,动态规划适用于子问题不是独立的情况,也就是各子问题包含公共的子子问题。在这种情况下,若用分治法则会做许多不必要的工作,即重复的求解公共的子子问题。动态规划算法对于每个子子问题只求解一次,将其结果保存在一张表中,从而避免每次遇到各个子问题时重新计算答案。

  动态规划算法的设计可以分为如下4个步骤:

1)描述最优解的结构;

2)递归定义最优解的值;

3)按自底向上的方式计算最优解的值;

4)由计算出的结果构造一个最优解。

采用动态规划方法的最优化问题中的两个要素是:最优子结构和重叠子问题

最优子结构:用动态规划方法求解优化问题的第一步是描述最优解的结构。如果问题的一个最优解中包含了子问题的最优解,则该问题具有最优子结构。在找寻最优子结构时,可以遵循一种共同的模式:

1)问题的一个解可以是做一个选择。做这种选择会得到一个或多个有待解决的子问题;

2)假设对一个给定的问题,已知的是一个可以导致最优解的选择。不必关心如何确定这个选择,尽管假定它是已知的。

3)在已知这个选择后,要确定哪些子问题会随之发生,以及如何最好地描述所得到的的子问题空间。

4)利用一种“剪贴”技术,来证明在问题的一个最优解中,使用的子问题的解本身也必须是最优的。

重叠子问题:适用于动态规划求解的最优化问题必须具有的第二个要素是子问题的空间要“很小”,也就是用来解原问题的递归算法可反复地解同样的子问题,而不是总是产生新的子问题。典型地,不同的子问题数是输入规模的一个多项式,当一个递归算法不断地调用同一问题时,我们说该最优问题包含重叠子问题。相反的,适用于分治法解决的问题往往在递归的每一步都产生全新的问题。动态规划算法总是充分利用重叠子问题,即通过每一个子问题只解一次,把解保存在一个在需要时就可以查看的表中,而每次查表的时间为常数。

应用动态规划求解最长公共子序列问题

1)描述一个最长公共子序列

设X={x1,x2,...,xm}和Y={y1,y2,...,yn}为两个序列,并设Z={z1,z2,...,zk}为X和Y的任意一个LCS。

如果xm等于yn,那么zk等于xm且等于yn,而且Zk-1是Xm-1和Yn-1的一个LCS;如果xm不等于yn,那么zk不同于xm蕴含Z是Xm-1和Y的一个LCS;如果xm不等于yn,那么zk不同于yn蕴含Z是X和Yn-1的一个LCS。

2)一个递归解

在找X和Y的一个LCS时,可能要检查一个或两个子问题,如果xm和yn相等,必须找出Xm-1和Yn-1的一个LCS,将xm=yn添加到这个LCS上,可以产生X和Y的一个LCS。如果xm不等于yn,就必须解决两个子问题:找出Xm-1和Y的一个LCS,以及找出X和Yn-1的一个LCS。这两个LCS中,较长的就是X和Y的一个LCS,因为这些情况涉及了所有的可能,其中一个最优的子问题解必被使用在X和Y的一个LCS中。

可以很容易地看出LCS问题中的重叠子问题性质。为找出X和Y的一个LCS,可能需要找出X和Yn-1的一个LCS以及Xm-1和Y的一个LCS。但这两个子问题都包含着找Xm-1和Yn-1的一个LCS的子子问题

#define M 100
#define N 100
char c[M][N];
int b[M][N];

void Lcs_length(char *s1,char *s2,int m,int n)            //计算公共子序列的长度
{
	for (int i=1;i<=m;i++)
	{
		c[i][0] = 0;
	}
	for (int i=0;i<=n;i++)
	{
		c[0][i] = 0;
	}
	for (int i=1;i<=m;i++)
	{
		for (int j=1;j<=n;j++)
		{
			if (s1[i-1]==s2[j-1])
			{
				c[i][j] = c[i-1][j-1]+1;
				b[i][j] = 0;
			}
			else if (c[i-1][j]>=c[i][j-1])
			{
				c[i][j] = c[i-1][j];
				b[i][j] = 1;
			}
			else
			{
				c[i][j] = c[i][j-1];
				b[i][j] = -1;
			}
		}
	}

}

void Print_lcs(char *s1,char *s2,int m,int n)            //打印出最长公共子序列
{
	if (m==0|n==0)
	{
		return;
	}
	if (b[m][n]==0)
	{
		cout<<s2[n-1]<<" ";
		Print_lcs(s1,s2,m-1,n-1);
	}
	else if (b[m][n]==1)
	{
		Print_lcs(s1,s2,m-1,n);
	}
	else Print_lcs(s1,s2,m,n-1);

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值