CF10D-LCIS题解--线性DP+打印方案

题目链接:

https://www.luogu.org/problemnew/show/CF10D

方法一

分析

\(LCS\)\(LIS\)已经成烂大街的知识了,可是当这两个合并起来成为\(LCIS\),解决的方式方法也多了起来.

首先有种最朴素的\(O(N^4)\)方法,\(f[i][j]\)表示A串第\(i\)个字母和B串第\(j\)个字母结尾的状态中\(LCIS\)的长度,那么

那么如果\(a[i]==b[j],f[i][j]=max_{0<=k<j且b[k]<a[i](b[j])}(f[i-1][k])+1\)

否则\(f[i][j]=f[i-1][j]\)

但是这种方法怎么打印方案呢?我们用\(path[j][len[j]]\)表示以\(j\)结尾的\(LCIS\)方案,\(len[j]\)指的是以\(j\)结尾的\(LCIS\)长度

这样我们从\(k\)更新到\(j\)时,首先将\(path[k][len[k]]\)全部复制到\(path[j][len[j]]\);

然后\(len[j]=len[k]+1,path[j][len[j]]=b[j]\)

跑时950+ms

代码

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cctype>
#include <queue>
#include <vector>
#define ll long long 
#define ri register int 
using std::max;
using std::min;
using std::swap;
template <class T>inline void read(T &x){
    x=0;int ne=0;char c;
    while(!isdigit(c=getchar()))ne=c=='-';
    x=c-48;
    while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-48;
    x=ne?-x:x;return ;
}
const int maxn=505;
const int inf=0x7fffffff;
int n,m,a[maxn],b[maxn],f[maxn][maxn],len[maxn];
int path[maxn][maxn];
void print(int x){
    for(ri i=1;i<=len[x];i++)printf("%d ",path[x][i]);
    puts("");
    return ;
}
int main(){
    int x,y,z;
    int ans=-inf,ed=0;
    read(n);
    for(ri i=1;i<=n;i++){read(a[i]);}
    read(m);
    for(ri i=1;i<=m;i++){read(b[i]);}
    a[0]=b[0]=-inf;
    for(ri i=1;i<=n;i++){
        for(ri j=1;j<=m;j++){
            if(a[i]==b[j]){
                for(ri k=0;k<j;k++){
                    if(b[k]<a[i]){
                        //f[i][j]=max(f[i][j],f[i-1][k]+1);
                        if(f[i][j]<f[i-1][k]+1){
                            f[i][j]=f[i-1][k]+1;
                            len[j]=len[k]+1;
                            for(ri p=1;p<=len[k];p++)path[j][p]=path[k][p];
                        }
                    }
                }
            }
            else f[i][j]=f[i-1][j];
            //ans=max(ans,f[i][j]);
            path[j][len[j]]=b[j];
            if(ans<f[i][j]){
                ans=f[i][j];
                ed=j;
            }
        }
    }
    printf("%d\n",ans);
    print(ed);  
    return 0;
}

方法二

我们考虑递推时的决策集合,\(f[i][j]\)都是由\(f[i][k](b[k]<a[i])\)递推得到,那么我们如果在从\(f[i][0]\)递推到\(f[i][j]\)时我们已经记录下所有\(f[i][k]\)的最大值设为\(val\),直接将\(f[i][j]\)设为\(max(f[i][j],val+1)\)就好了,打印路径的方法跟方法一类似

这样时间复杂度能少个\(N\)

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <queue>
#include <algorithm>
#define ll long long 
#define ri register int 
#define ull unsigned long long 
using std::min;
using std::max;
using std::swap;
template <class T>inline void read(T &x){
    x=0;int ne=0;char c;
    while(!isdigit(c=getchar()))ne=c=='-';
    x=c-48;
    while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-48;
    x=ne?-x:x;return ;
}
const int maxn=505;
const int inf=0x7ffffff;
int n,m,a[maxn],b[maxn],f[maxn][maxn],path[maxn][maxn],len[maxn],ed;
int main(){
    read(n);
    for(ri i=1;i<=n;i++){
        read(a[i]);
    }
    read(m);
    for(ri i=1;i<=m;i++){
        read(b[i]);
    }
    int ans=-inf,val,lst=0;
    for(ri i=1;i<=n;i++){
        lst=0;
        val=f[i-1][0];
        for(ri j=1;j<=m;j++){
           if(a[i]==b[j]){
              if(val+1>f[i][j]){
                 f[i][j]=val+1;
                 for(ri k=1;k<=len[lst];k++)path[j][k]=path[lst][k];
                 len[j]=len[lst]+1;          
              }
           }
           else f[i][j]=f[i-1][j];
           path[j][len[j]]=b[j];
           //ans=max(ans,f[i][j]);
           if(f[i][j]>ans){
            ans=f[i][j];
            ed=j;
           }
           if(b[j]<a[i]){
                //val=max(val,f[i-1][j]);
                if(val<f[i-1][j]){
                    val=f[i-1][j];
                    lst=j;
                }
           }
        }
    }   
    printf("%d\n",ans);
    //printf("%d %d\n",ed,len[ed]);
    for(ri i=1;i<=len[ed];i++)printf("%d ",path[ed][i]);
    return 0;
}

当然题解中还有\(O(1)\)记录路径的方法,以及\(O(N)\)的空间复杂度方法,这里先挖个坑吧

猜你喜欢

转载自www.cnblogs.com/Rye-Catcher/p/9579397.html