太坑了所以决定写下来= =
对于两个数列,我们要求他们的最长上升公共子序列该怎么求呢?
用Dp来做,我们很容易想到F[ i ][ j ]来表示A的前i项和B的前j项可以组成的最长上升公共子序列。
并且易得出转移方程:
a[i]!=b[j]时:F[i][j]=F[i-1][j]
a[i]==b[j]时:F[i][j]=max(F[i-1][k])+1 (1=<k<j)&&b[j]>b[k]//保证上升
所以我们可以很容易写出一个O(n^3)的代码
for(i=1;i<=n1;i++)
{
for(j=1;j<=n2;j++)
{
if(a[i]!=b[j])f[i][j]=f[i-1][j];
else
{
for(k=1;k<j;k++)
if(b[j]>b[k])f[i][j]=max(f[i][j],f[i-1][k]);//dp
f[i][j]+=1;
}
}
}
Max=0;
for(i=1;i<=n1;i++)
for(j=1;j<=n2;j++)
Max=max(f[i][j],Max); //取出答案
时间复杂度O(n^3),空间复杂度O(len^2)
我们分析下,这个Dp是要取max(F[i-1][k]) 那么我们很自然就想到单调队列来优化
for(i=1;i<=n1;i++)
{
max=0;
for(j=1;j<=n2;j++)
{
f[i][j]=f[i-1][j];
if(a[i]>b[j]&&max<f[i-1][j])max=f[i-1][j];
if(a[i]==b[j])f[i][j]=max+1;
}
}
max=0;
for(i=1;i<=n2;i++)
if(max<f[n1][i])max=f[n1][i];
来解释下,我们开个Max来维护,i在外围循环,j在内围,所以要求出F[i][1]的前提是先求出F[i-1][1]~F[i-1][n2]的值
而上面的代码,对于a[i]>b[j]时,更新max的值,对于后面的a[i]==b[j]时,这个a[i]是没有改变的,也就是说:a[i]=b[j]>b'[j],那么此时的b[j]一定比前面的b[j]更大
我们就不用拿再一个for循环去求max(F[i-1][k])了!!!(等价于一个单调队列)
时间复杂度O(n^2),空间复杂度O(len^2)
对于上面的代码,我们观察后发现,如果len一大,那么二维数组很可能爆内存,那么有没有什么方法可以优化空间呢?
for(i=1;i<=n1;i++)
{
max=0;
for(j=1;j<=n2;j++)
{
if(a[i]>b[j]&&max<f[j])max=f[j];
if(a[i]==b[j])f[j]=max+1;
}
}
for(j=1;j<=n2;j++)if(ans<f[j])ans=f[j];
printf("%d\n",ans);
上面这段代码,我们发现F[]少了一维,却还是能A,因为每次求出F[i][j]后,对于后面的F[i+1][j]并不影响
比如:
F[ ][ ]=1 1 2 2 2 当我们改成一维数组时,i=1时 有F[ ]= 1 1 2 2 2,
3 3 4 4 4
当i=2时,我们的F[ ]已经赋好了初值了,所以我们相当于在上一次的基础上直接进行操作,得到F[ ]=3 3 4 4 4
所以二维数组就变成了一维的
时间复杂度O(n^2),空间复杂度O(len)
开始的时候怎么都想不到后面两种,多亏果神指点,%%%
相关题目:
http://oi.nks.edu.cn/showproblem?problem_id=1992 【DP】配饰
http://oi.nks.edu.cn/showproblem?problem_id=1035 咒语