LIS,LCS,LCIS

LIS(最长上升子序列),LCS(最长公共子序列)是动态规划中基础的两个问题,今天连同他们的联合版LCIS一起总结一下。
LIS(最长上升子序列):有两种方法,一种DP(n*n),还有一种解法复杂度是贪心+二分(nlogn)
法一:

//dp[i]表示以i结尾的最长上升子序列
for(int i=1;i<=n;i++)
{
    dp[i]=1;
    for(int j=1;j<i;j++)
        if(a[i]>a[j])   //如果当前这个值大于了a[j],那么dp[i]就可以由dp[j]所代表的最长上升子序列的集合加上当前的a[i]转移过来。
            dp[i]=max(dp[i],dp[j]+1);
}
int ans=-1;
for(int i=1;i<=n;i++)  //遍历一遍以哪个数结尾的最长上升子序列值最大
    ans=max(ans,dp[i]); 

法二:

//dp[i]表示以i结尾的最长上升子序列,num存储不降序列,len表示不降序列的长度
int len=0,num[SIZE],a[SIZE];
num[++len]=a[1];
for(int i=2;i<=n;i++)
{
    if(a[i]>num[len]) num[++len]=a[i];
    else
    {
        int pos=lower_bound(num,num+1+len,a[i])-num;
        num[pos]=a[i];
    }
}
//最后的len即为答案;

LCS(最长公共子序列):
dp[i][j]表示数组a中1到i,数组b中1到j的序列的最长公共子序列

for(int i=1;i<=n;i++)
{
    for(int j=1;j<=n;j++)
    {
        if(a[i]==b[j]) dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1);  //进行决策
        else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
    }
}

LCIS(最长公共上升子序列)这个结合了LIS和LCS,在dp方程的含义上也有二者的结合。
dp[i][j]表示数组a的1到i的序列中,以b[j]结尾的最长公共上升子序列,可以看到,dp[i][j]其实表示的是一个集合,包括了a[1]b[j],a[2]b[j]…a[i]b[j]这些集合,其中的最长的公共上升子序列的长度就存储在dp[i][j]中。为什么要这样规定,因为我们要找上升子序列,就需要知道以前的状态的最后一个值是多少,我们才可以把状态给更新下去。
所以枚举到i,j时,我们分两部分考虑,这两部分能够不重不漏的包括所有情况,一种是a[i]=b[j],另一种相反
1.a[i]!=b[j],dp[i][j]表示数组a的1到i的序列中,以b[j]结尾的最长公共上升子序列,因为a[i]!=b[j],我们又要求以b[j]结尾,所以去掉a[i]后结果一定是不变的,反着来说就是加上a[i]后对dp[i][j]表示的最长长度不会发生改变,那么我们只看数组a前i个结果一定不会变,也就是dp[i][[j]=dp[i-1][j];
2.a[i]==b[j]:此时我们如果把a[i]去掉那么就会对结果产生影响。举个例子:
2,5,6,9
3,4,5,7
我们假设i=2,j=3,除去a[2],只看a[1]时,dp[1][3]=0;然而加入了a[2]后,dp[2][3]就变成了1;对结果产生了影响。
接着上面的说,如果此时a[i]==b[j],那么dp[i][j]一定是max(dp[i-1][k]+1), 1=<k<j且b[k]<b[j],大致的意思就是我们从以前的状态中找一个最大值再加上当前a[i]=b[j]这一对又可以更新最长公共上升子序列的长度。
时间复杂度:O(nnn)

for(int i=1;i<=n;i++)
{
    for(int j=1;j<=n;j++)
    {
        dp[i][j]=dp[i-1][j];
        if(a[i]==b[j])
        {
        	for(int k=1;k<j;k++)
        		if(a[i]<b[j])
        			dp[i][j]=max(dp[i][j],dp[i-1]][k]+1);
        }
    }
}
int ans=0;
for(int i=1;i<=n;i++)
	ans=max(ans,dp[n][i]);  //遍历一遍看以b[?]结尾的值最大,ans即是答案。

优化:

for(int k=1;k<j;k++)
 		if(b[k]<b[j])  //由上可知b[j]=a[i];
 			dp[i][j]=max(dp[i][j],dp[i-1]][k]+1);

这一段代码其实就是在找一个最大值,并且这个最大值是可以维护的。所以对于每一个i,我们只需要维护当前i下,所有b[j]小于a[i]时的maxv就可以优化掉一重循环。

for(int i=1;i<=n;i++)
{
     int maxv=1;
     for(int j=1;j<=n;j++)
     {
         dp[i][j]=dp[i-1][j];
         if(a[i]==b[j]) dp[i][j]=max(maxv,dp[i][j]);
         if(a[i]>b[j]) maxv=max(maxv,dp[i-1][j]+1);
     }
 }

END

发布了33 篇原创文章 · 获赞 14 · 访问量 420

猜你喜欢

转载自blog.csdn.net/qq_44077455/article/details/103605300