zoukankan      html  css  js  c++  java
  • 算法导论_动态规划_最长公共子序列

    一、动态规划的概念

            动态规划(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]是相等的,都是空字符,这显然是不对的,因为他们不存在公共子序列,因此上面的代码是在原代码的基础上改进得到,依然感谢原博主的代码。

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

      夜深了,黑暗即将降临

  • 相关阅读:
    HDU 4801 Pocket Cube
    HDU 5008 Boring String Problem(后缀数组+二分)
    2-Sat问题
    后缀数组
    树形DP
    图论
    SRM 628 DIV2
    组合博弈
    Github使用笔记
    VS2010+OpenCV配置
  • 原文地址:https://www.cnblogs.com/1242118789lr/p/7403568.html
Copyright © 2011-2022 走看看