动态规划--LIS与LCS

动态规划

  • 动态规划
    • 动态规划DP的基本描述
    • 动态规划的核心
    • 动态规划与贪心算法分治的区别
    • LIS最长递增子序列问题


1. 动态规划(DP)的基本描述

dynamic programming is a method for solving a complex problem by breaking it down into a collection of simpler subproblems.

动态规划是通过拆分问题,定义问题状态和状态之间的关系,使得问题能够以递推(或者说分治)的方式去解决。


2. 动态规划的核心

动态规划的本质,是对问题状态的定义状态转移方程的定义

动态规划是对于 某一类问题 的解决方法!!重点在于如何鉴定“某一类问题”是动态规划可解的

状态:状态用来描述该问题的子问题的解。根据子问题来定义状态。详细可见动态规划的意义

lis的重新定义:给定一个数列,长度为N,
设F_{k}为:以数列中第k项结尾的最长递增子序列的长度. 求F_{1}..F_{N}中的最大值.

状态转移方程:
上述状态定义好之后,状态和状态之间的关系式,就叫做状态转移方程。

状态转移方程为:

F(1) = 1 (根据状态定义导出边界情况)
F(k)=max(F(i)+1 | A(k)>A(i), i属于
(1..k-1)) // (k>1)

动态规划算法分以下4个步骤:

  1. 状态是什么
  2. 状态转移方程是什么
  3. 状态的初始值是什么
  4. 问题要求的最后答案是什么
    每个步骤分析完成之后,就基本上解决了整道动态规划的问题。

动态规划的常见类型分为如下几种:

  • 矩阵型
  • 序列型
  • 双序列型
  • 划分型
  • 区间型
  • 背包型
  • 状态压缩型
  • 树型

3. 动态规划与贪心算法,分治的区别

每个阶段只有一个状态->递推;

每个阶段的最优状态都是由上一个阶段的最优状态得到的->贪心

每个阶段的最优状态是由之前所有阶段的状态的组合得到的->搜索;

每个阶段的最优状态可以从之前某个阶段的某个或某些状态直接得到而不管之前这个状态是如何得到的->动态规划。

推荐一个网站,可以帮助小白理解动态规划小白入门动态规划


4. LIS(最长递增子序列问题)

最长上升子序列问题,也就是Longest increasing subsequence,缩写为LIS

  • 算法课上讲的问题。
  • 这个上升实质上就是一个对<进行定义的过程,所以我们求解的其实是一类问题,也就是在给定序列中求解长度最长的符合某一性质的子序列的问题。

给出一个例子
5 , 3, 4, 8, 6 , 7

O(n^2)的动态规划方法

dp[i]表示以i结尾的子序列中LIS的长度。然后我用dp[j] (0<= j < i) 来表示在i之前的LIS的长度。然后我们可以看到,只有当a[i]> a[j]的时候,我们需要进行判断,是否将a[i]加入到dp[j]当中。为了保证我们每次加入都是得到一个最优的LIS,有两点需要注意:第一,每一次,a[i]都应当加入最大的那个dp[j],保证局部性质最优,也就是我们需要找到max(dp[j] (0 <= j < i));第二,每一次加入之后,我们都应当更新dp[j]的值,显然,dp[i]=dp[j]+1。 如果写成递推公式,我们可以得到dp[i] = max(dp[j](0 <=j < i)) + (a[i]>a[j]?1:0)。 于是我们就能够得到O(n^2)的动态规划方法的实现:

const int MAXN = 1010;
int n;
int a[MAXN];
int dp[MAXN];

int lis()
{
    memset(dp, 0, sizeof(dp));
    int Max;
    for (int i = 0; i < n; ++i)
    {
        Max = 0;
        for (int j = 0; j < i; ++j)
        {
            if (a[i] > a[j])
            {
                Max = max(Max, dp[j]);
            }
        }
        dp[i] = Max + 1;
    }
    Max = 0;
    for (int i = 0; i < n; ++i)
    {
        if (dp[i] > Max)    Max = dp[i];
    }
    return Max;
}

O(nlogn)的动态规划+二分方法
在前一种方法中,我们花费了很多时间在寻找最大的dp[j]上。如果有办法让这个dp[j]变成一个递增的序列,我们就能使用二分来进行优化,从而使得复杂度下降为O(nlogn)了

设置一个数组a[i]
存放所有长度为i的上升子序列中最小的末元素值,

那么当来一个新数data时,如果它的值大于最长长度的末元素的值(即a[ans]),则ans++;且a[ans]=data;
否则,通过二分查找(数组a中的元素为递增),将最接近data且大于data的那个元素更新为data,既最小的大于它的数。

依旧是刚刚的5,3,4,8,6,7
5: a[1]= 5
3: 找最大的a[i],3<5 , a[1]=3
4 : 找最大的a[i] ,4>3, a[2]= 4
8: 找最大的a[i],8>4 a[3] = 8
6: 在a[i]中找6排的位置,4< 6< 8,
a[3] =6 ;
7 : 在a[i]中找7排的位置,7> 6, a[4] =7 ;


int main()
{
    int t, n, num;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d %d", &n, &num);
        res[0]=num;
        int tot = 1;
        for(int i=1;i"%d", &num);
            if(num>=res[tot-1])
            res[tot++]=num;
            else
            {
                int pos=binSearch(0,tot-1,num);      //找到最小的大于它的数
                res[pos+1] = num;
            }  
        }
        printf("%d\n", tot);
    }
    return 0;
}

–持续更新,一点一点的写。

你可能感兴趣的:(动态规划--LIS与LCS)