算法导论_动态规划_最长公共子序列

一、动态规划的概念

动态规划(Dynamic Programming)是通过组合子问题的解而解决整个问题的。分治是指将问题划分成一些独立的子问题,递归地求解各子问题,然后合并子问题的解而得到原始问题的解,与此不同,动态规划适用于子问题不是独立的情况,也就是各个子问题包含公共的子问题。在这种情况下,采用分治法会做许多不必要的工作,即重复地求解公共地子问题。动态规划算法对每个子问题只求解一次,将其结果保存在一张表中,从而避免每次遇到各个子问题时重新计算答案。

动态规划通常应用于最优化问题。此类问题可能有很多种可行解。每个解有一个值,而我们希望找出具有最优(最大或最小)值的解。称这样的解为该问题的“一个”最优解,而不是“确定的”最优解,因为可能存在多个取最优值的解。

为了节约重复求相同子问题的时间,引入一个数组,不管他们是否对最终解有用,把所有子问题的解存于该数组中,这就是动态规划所采用的基本方法。

二、动态规划求解的一般步骤

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

1> 描述最优解的结构。

2> 递归定义最优解的值。

3> 按自底向上的方式计算最优解的值。

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

算法导论这本书上对动态规划的描述很详细,并举了好几个例子,下面贴出用动态规划算法求解最长公共子序列的代码:

#include<iostream>
#include<vector>
#include<utility>    //为返回两个二维数组,使用pair
using namespace std;

pair<int**,int**> Lcs_Length(const string &strX, const string &strY)
{

	int nXlen = strX.length();
	int nYlen = strY.length();

	//分配二维数组的两种方法
	//int** C = (int**)malloc((nXlen + 1) * sizeof(int*));
	//for (int i = 0; i<= nXlen; i++)
	//	C[i] = (int*)malloc((nYlen + 1) * sizeof(int));

	int** C = new int*[nXlen + 1];
	for (int i = 0; i<= nXlen; i++)
		C[i] = new int[nYlen + 1];

	int** B = new int*[nXlen + 1];
	for (int i = 0; i <= nXlen; i++)
		B[i] = new int[nYlen + 1];

	for (int i = 0; i <= nXlen; i++)
	{
		C[i][0] = 0;
	}

	for (int j = 0; j <= nYlen; j++)
	{
		C[0][j] = 0;
	}

	for (int i = 0; i != nXlen; i++)
	{
		for (int j = 0; j != nYlen; j++)
		{
			if (strX[i] == strY[j])
			{
				C[i + 1][j + 1] = C[i][j] + 1;
				B[i + 1][j + 1] = 0;  //说明该公共元素在LCS中
			}
			else
			{
				if (C[i][j+1] >= C[i+1][j])
				{
					C[i + 1][j + 1] = C[i][j + 1];
					B[i + 1][j + 1] = 1;   //! 左移
				}
				else
				{
					C[i + 1][j + 1] = C[i + 1][j];
					B[i + 1][j + 1] = -1; //! 右移
				}
			}
		}
	}
	pair<int**, int**> result(C, B);
	return result;
}

void PrintAllCase(pair<int**, int**> result, const string &strX, int i, int j)
{
	if (i == 0 || j == 0)
	{
		return;
	}

	if (result.second[i][j] == 0)
	{
		PrintAllCase(result, strX, i - 1, j - 1);
		cout << strX[i - 1];
	}
	else if (result.second[i][j] == 1)
	{
		PrintAllCase(result, strX, i - 1, j);
	}
	else
	{
		PrintAllCase(result, strX, i, j - 1);
	}
}
int main()
{
	string strX = "10010101";
	string strY = "010110110";

	pair<int**, int**> result = Lcs_Length(strX, strY);
	cout << "The max length of LCS is " << result.first[strX.length()][strY.length()] << endl;

	cout << "The LCS are : ";
	PrintAllCase(result, strX, strX.length(), strY.length());
	cout << endl;
    return 0;
}

  此代码有参考这篇博客:http://blog.csdn.net/houqd2012/article/details/39928159

但是这篇博客的代码有点问题,问题在于在C++中,数组或者字符串A的第一个元素是A[0],而不是A[1],而在算法导论这本书上基本所有的伪代码都默认第一个元素是A[1],这将导致许多问题的发生,比如在求最长公共子序列的问题上,当X="A",Y="B"时,上面提到的那篇博客中的代码运行出来的结果是最长公共子序列的长度为1,因为X[1]和Y[1]是相等的,都是空字符,这显然是不对的,因为他们不存在公共子序列,因此上面的代码是在原代码的基础上改进得到,依然感谢原博主的代码。

写代码是一种艺术,需要思考,最美妙的地方在于把自己的思想转化为可以运行的代码。

夜深了,黑暗即将降临

时间: 2024-10-25 17:32:01

算法导论_动态规划_最长公共子序列的相关文章

算法导论--动态规划(最长公共子序列)

最长公共子序列问题(LCS) 给定两个序列X=?x1,x2,x3...xm?和Y=?y1,y2,y3...xn?,求X和Y的最长公共子序列. 例如:X=?A,B,C,B,D,A,B?,和Y=?B,D,C,A,B,A?,的最长公共子序列为?B,C,B,A?,长度为4: 对于此问题,可以采用暴力求解的方式来比对,即穷举出X的所有子序列,用每个子序列与y做一 一比较.假如X序列共有m个元素,对每个元素可以决定选或不选,则X的子序列个数共有2m个,可见与长度m呈指数阶,这种方法效率会很低. 动态规划 前

【算法导论之七】动态规划求解最长公共子序列

一.动态规划的概念 动态规划(Dynamic Programming)是通过组合子问题的解而解决整个问题的.分治算法是指将问题划分成一些独立的子问题,递归地求解各子问题,然后合并子问题的解而得到原始问题的解,与此不同,动态规划适用于子问题不是独立的情况,也就是各个子问题包含公共的子问题.在这种情况下,采用分治法会做许多不必要的工作,即重复地求解公共地子问题.动态规划算法对每个子问题只求解一次,将其结果保存在一张表中,从而避免每次遇到各个子问题时重新计算答案. 动态规划通常应用于最优化问题.此类问

算法——动态规划篇——最长公共子序列

问题描述      最长公共子序列,英文缩写为LCS(Longest Common Subsequence).其定义是,一个序列 S ,如果分别是两个或多个已知序列的子序列,且是所有符合此条件序列中最长的,则 S 称为已知序列的最长公共子序列.       解决最长公共子序列,一种常用的办法,就是穷举法,组合出所有的情况,但是这样对于长序列的情况来说,是非常不实际.. 假设现在有两个序列,x[]={'A','B','C','B','D','A','B'};y[]={'B','D','C','A'

动态规划解决最长公共子序列问题(转)

原文链接 动态规划法 经常会遇到复杂问题不能简单地分解成几个子问题,而会分解出一系列的子问题.简单地采用把大问题分解成子问题,并综合子问题的解导出大问题的解的方法,问题求解耗时会按问题规模呈幂级数增加. 解决思想: 为了节约重复求相同子问题的时间,引入一个数组,不管它们是否对最终解有用,把所有子问题的解存于该数组中,这就是动态规划法所采用的基本方法. [问题] 求两字符序列的最长公共字符子序列 问题描述:字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后

动态规划解最长公共子序列问题(转)

 动态规划法 经常会遇到复杂问题不能简单地分解成几个子问题,而会分解出一系列的子问题.简单地采用把大问题分解成子问题,并综合子问题的解导出大问题的解的方法,问题求解耗时会按问题规模呈幂级数增加. 为了节约重复求相同子问题的时间,引入一个数组,不管它们是否对最终解有用,把所有子问题的解存于该数组中,这就是动态规划法所采用的基本方法. [问题] 求两字符序列的最长公共字符子序列 问题描述:字符序列的子序列是指从给定字符序列中随意地(不一定连续)去掉若干个字符(可能一个也不去掉)后所形成的字符序列

算法导论--------------LCS问题(最长公共子系列)

1.基本概念 一个给定序列的子序列就是该给定序列中去掉零个或者多个元素的序列.形式化来讲就是:给定一个序列X={x1,x2,--,xm},另外一个序列Z={z1.z2.--,zk},如果存在X的一个严格递增小标序列<i1,i2--,ik>,使得对所有j=1,2,--k,有xij = zj,则Z是X的子序列.例如:Z={B,C,D,B}是X={A,B,C,B,D,A,B}的一个子序列,相应的小标为<2,3,5,7>.从定义可以看出子序列直接的元素不一定是相邻的. 公共子序列:给定两个

[算法]动态规划之最长公共子序列

最长公共子序列 1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<time.h> 4 #include<string.h> 5 6 #define N 5 7 8 int max(int a, int b, int c) { 9 10 int ab = a>b ? a : b; 11 return ab > c ? ab : c; 12 13 } 14 15 void solve(int *a

动态规划之最长公共子序列(LCS)

tips : 本文内容是参考了很多著名博客和自己的思考得出的,如有不当欢迎拍砖. 先简单说一下动态规划 通俗地说:动态规划就是将一个可以划分为子问题的问题进行递归求解,不过动态规划将大量的中间结果保存起来, 不管它们是否会用得到,从而在后面的递归求解过程中可以快速求解.由此可以看得出来动态规划是一个以牺牲空间 为代价换取时间的算法. 对于最长公共子序列的题目不用多说,现在来分析一下LCS的动态规划解决思路: 一.首先先观察问题是否符合动态规划最明显的两个特征:最优子结构和重叠子问题 方便起见,以

算法重拾之路——最长公共子序列(LCS)

***************************************转载请注明出处:http://blog.csdn.net/lttree******************************************** 第二章:动态规划 最长公共子序列 算法描述: 一个给定序列的子序列是该序列中删去若干元素后得到的序列.确切的说,若给定序列 X={ x1,x2,...,xm },则另一序列 Z = { z1,z2, ... ,zk },是X的子序列是指存在一个严格递增下标序列

动态规划求解最长公共子序列

动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解.与分治法不同的是,适合于用动态规划求解的问题,经分解得到的子问题往往不是互相独立的.若用分治法来解决这类问题,则分解得到的子问题数目太多,以至于最后解决原问题需要耗费指数时间.然而,不同子问题的数目常常只有多项式量级.在用分治法求解时,有些子问题被重复计算了许多次.如果我们能够保存已解决的子问题的答案,而在需要时再找出已求得的答案,这样就可以避免大量的重复计算,从而得到多项式