通过最长公共子序列和矩阵连乘来分析DP和备忘录

DP是什么

将一个问题拆成几个子问题,分别求解这些子问题,即可推断出大问题的解

定义:

动态规划算法是通过拆分问题,定义问题状态和状态之间的关系,使得问题能够以递推(或者说分治)的方式去解决。
动态规划算法的基本思想与分治法类似,也是将待求解的问题分解为若干个子问题(阶段),按顺序求解子阶段,前一子问题的解,为后一子问题的求解提供了有用的信息。在求解任一子问题时,列出各种可能的局部解,通过决策保留那些有可能达到最优的局部解,丢弃其他局部解。依次解决各子问题,最后一个子问题就是初始问题的解。

最长公共子序列(LCS)

题目

给出两个字符串,求出这样的一个最长的公共子序列的长度:子序列中的每个字符都能在两个原串中找到, 而且每个字符的先后顺序和原串中的先后顺序一致。

分析

包含重复子问题:
我们要求x1xi,y1yj的LCS,那么是不是要求x1xi-1,y1yi-1的LCS
我们要求x1xi-1,y1yi-1的LCS,那么是不是要求x1xi-2,y1yi-2的LCS
所以我们要求的x1xi,Y1Yj的LCS这个大问题中,包含了很多的重复子问题

核心

c[i][j]表示x1xi,y1yi的LCS序列长度
if(x[i]==y[j])
c[i][j]=c[i-1][j-1]+1
else
c[i][j]=Math.max(c[i-1][j],c[i][j-1])
if(i==0 || j==0)
c[i][j]=0

代码

package com.pub;
 
import java.util.ArrayList;
import java.util.List;
 
public class LCS {
	 public static List<String> getLCSstring(char[] str1, char[] str2) {
	        int i, j;
	        int len1, len2;
	        len1 = str1.length;
	        len2 = str2.length;
	        int maxLen = len1 > len2 ? len1 : len2;
	        int[] max = new int[maxLen];
	        int[] maxIndex = new int[maxLen];
	        int[] c = new int[maxLen];
	        List<String> list = new ArrayList<>();
            //生成矩阵
	        for (i = 0; i < len2; i++) {
	            for (j = len1 - 1; j >= 0; j--) {
	                if (str2[i] == str1[j]) {
	                    if ((i == 0) || (j == 0))
	                        c[j] = 1;
	                    else
	                        c[j] = c[j - 1] + 1;
	                } else {
	                    c[j] = 0;
	                }
 
	                if (c[j] > max[0]) {   //如果是大于那暂时只有一个是最长的,而且要把后面的清0;
	                    max[0] = c[j];
	                    maxIndex[0] = j;
 
	                    for (int k = 1; k < maxLen; k++) {
	                        max[k] = 0;
	                        maxIndex[k] = 0;
	                    }
	                } else if (c[j] == max[0]) {   //有多个是相同长度的子串
	                    for (int k = 1; k < maxLen; k++) {
	                        if (max[k] == 0) {
	                            max[k] = c[j];
	                            maxIndex[k] = j;
	                            break;  //在后面加一个就要退出循环了
	                        }
 
	                    }
	                }
	            }
	        }
 
	        for (j = 0; j < maxLen; j++) {
	            if (max[j] > 0) {
 
	                StringBuffer sb = new StringBuffer();
	                for (i = maxIndex[j] - max[j] + 1; i <= maxIndex[j]; i++) {
	                    sb.append(str1[i]);
	                }
	                String lcs = sb.toString();
	                list.add(lcs);
	            }
	        }
	        return list;
	    }
 
	    public static void main(String[] args) {
 
	        String str1 = new String("adbba12345");
	        String str2 = new String("adbbf1234sa");
	        List<String> list = getLCSstring(str1.toCharArray(), str2.toCharArray());
	        for (int i = 0; i < list.size(); i++) {
	            System.out.println("第" + (i + 1) + "个公共子串:" + list.get(i));
	        }
 
	        str1 = new String("adbab1234");
	        str2 = new String("adbbf123s4a");
	        list = getLCSstring(str1.toCharArray(), str2.toCharArray());
	        for (int i = 0; i < list.size(); i++) {
	            System.out.println("第" + (i + 1) + "个公共子串:" + list.get(i));
	        }
 
	    }
}

矩阵连乘

题目

给定n个矩阵{A1,A2…An},其中Ai与Ai+1是可以相乘的,判断这n个矩阵通过加括号的方式相乘,使得相乘的次数最少!
例如:

计算三个矩阵连乘{A1,A2,A3};维数分别为10100 , 1005 , 5*50

按此顺序计算需要的次数((A1A2)A3):10X100X5+10X5X50=7500次

按此顺序计算需要的次数(A1(A2A3)):10X5X50+10X100X50=75000次

所以要解决的问题是:如何确定矩阵连乘积A1A2,……An的计算次序,使得按此计算次序计算矩阵连乘积需要的数乘次数达到最小化。

分析

假设我们把这个问题看做一个多阶段决策问题,每次的决策就是选一个位置加一个括号。
假设我们已经得到最优的解了。那么这个最优解的最后一次乘法,一定可以看成是两个部分的乘积。而两个部分又可以看做是原问题的规模减小后的问题。
定义OPT(i,j)表示AiAi+1…Aj ​所需的最少的乘法次数。原问题其实就是在求OPT(1,n)

而将原问题分解为两个子问题:OPT(1,k)和OPT(k,n),那么原问题的解可以通过O PT(1,n)=OPT(1,k)+OPT(k,n)+p0×pk×pn 得到。

伪代码

find_min_alpha(1,n)
	for i=1 to n do
		table[i,i]=0
	end for
	for j=2 to n do
		for i=1 to n-j do
			min=p[i-1]p[i]p[i+1]
			for k=i to i+j do
				tmp =table[i,k]+table[k+1,i+j] +p[i-1]p[k]p[i+j]
				if tmp<min then
					min=tmp
				end if
			end for
			table[i,i+j]=min
		end for
	end for	
	return table[1,n]

posted @ 2021-03-24 11:28  唐坣  阅读(111)  评论(0)    收藏  举报