hdu 1423 Greatest Common Increasing Subsequence(最长公共上升子序列、LCIS)

题目:http://acm.hdu.edu.cn/showproblem.php?pid=1423

题目大意:给你两个序列,让你求他们的LCIS。

解题思路:假设两个序列分别为num1,num2。

        首先,如果做过LCS,应该马上会想到用 d[ i ][ j ] 来表示序列 num1 的前 i 个、序列 num2 的前 j 个的最长的长度,但是转换的时候,因为是上升,并不知道这个 d[ i - 1 ][ j - 1 ] 转换过来的时候那个值是不是比 当前的小,所以我们这样设计状态d [ i ][ j ],就是序列 num1 的前 i 个,num2 的前 j 个且以num2[ j ] 为结尾的LCIS,那么还需要在 for 一遍,比num1[ i ]( 也就是 num2[ j ]) 小的转换过来。 

代码是这样的(时间复杂度O(n^3),空间复杂度O(n^2)):

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int MAXN = 555;

int num1[MAXN],num2[MAXN];
int d[MAXN][MAXN];

int main()
{
    int cas = 0;
    int _;
    scanf("%d",&_);
    while(_--)
    {
        if(cas++ > 0) puts("");
        int n1,n2;
        scanf("%d",&n1);
        for(int i = 1;i <= n1;i++) scanf("%d",&num1[i]);
        scanf("%d",&n2);
        for(int i = 1;i <= n2;i++) scanf("%d",&num2[i]);
        memset(d,0,sizeof(d));
        for(int i = 1;i <= n1;i++)
        {
            for(int j = 1;j <= n2;j++)
            {
                d[i][j] = d[i-1][j];
                if(num1[i] == num2[j])
                {
                    d[i][j] = max(d[i][j],1);
                    for(int k = 1;k < j;k++)
                        if(num2[j] > num2[k]) d[i][j] = max(d[i][j],d[i-1][k]+1);
                }
                //printf("i = %d,j = %d,d = %d\n",i,j,d[i][j]);
            }
        }
        int ans = 0;
        for(int i = 1;i <= n2;i++) ans = max(ans,d[n1][i]);
        printf("%d\n",ans);
    }
    return 0;
}

        有没有觉得就是最里面那个 k 循环那里搞得时间复杂度有点高,是的,其实仔细想想会发现那一层其实可以去掉,但是去掉,那状态转移的时候怎么知道大小关系?看第一层循环,每次都是 num1[ i ] 来更新num2,而且 k 又是 num2 j 走过的部分再走一遍,那么我们可以设一个变量 m 作为之前 j 走过的当前 j 要用到的最大值,这样就不用走 k 循环了。由于都是num1[ i ] 来更新,它更新的肯定是num1[ i ] == num2[ j ] 的 d 数组,也就是我们拿 num1[ i ] 和 num2[ j ] 比较,如果 num1[ i ] > num2[ j ] && d[ i - 1 ][ j ] > m ,那么就更新这个 m,然后如果再相等,就直接用 m 更新就好了,这样时间复杂度就降为 O(n^2)了。但是如果你用上面的第一种方式来定义的,个人觉得就不能这样纸优化了。

代码是这样的(时间复杂度:O(n^2),空间复杂度O(n^2)):

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int MAXN = 555;

int num1[MAXN],num2[MAXN];
int d[MAXN][MAXN];

int main()
{
    int cas = 0;
    int _;
    scanf("%d",&_);
    while(_--)
    {
        if(cas++ > 0) puts("");
        int n1,n2;
        scanf("%d",&n1);
        for(int i = 1;i <= n1;i++) scanf("%d",&num1[i]);
        scanf("%d",&n2);
        for(int i = 1;i <= n2;i++) scanf("%d",&num2[i]);
        memset(d,0,sizeof(d));
        for(int i = 1;i <= n1;i++)
        {
            int m = 0;
            for(int j = 1;j <= n2;j++)
            {
                d[i][j] = d[i-1][j];
                if(num1[i] > num2[j] && d[i-1][j] > m) m = d[i-1][j];
                if(num1[i] == num2[j]) d[i][j] = m+1;
            }
        }
        int ans = 0;
        for(int i = 1;i <= n2;i++) ans = max(ans,d[n1][i]);
        printf("%d\n",ans);
    }
    return 0;
}

        时间上已经优化为了 O(n^2)了,其实,空间上还能优化。

        如果不相等就是 d[ i ][ j ] = d[ i - 1 ][ j ],相等就是 m+1,而且 m 也只是在更新d[ j ] 之后更新的,也就是此时 d[ j ] 存的还是 d[ i - 1][ j ] ,那么我们就只用设一维d[ j ],如果不相等就不用变,如果相等,就是 m+1。

        但是说回来,其实空间优化不用怎么计较,时间过了,空间也差不多能过,要不然,你空间优化,因为它只跟上一层有关,第一维搞成 0、1 变化的不就行了。所以推荐还是使用上面那个,清晰一点。

代码是这样的(时间复杂度O(n^2),空间复杂度O(n)):

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int MAXN = 555;

int num1[MAXN],num2[MAXN];
int d[MAXN];

int main()
{
    int cas = 0;
    int _;
    scanf("%d",&_);
    while(_--)
    {
        if(cas++ > 0) puts("");
        int n1,n2;
        scanf("%d",&n1);
        for(int i = 1;i <= n1;i++) scanf("%d",&num1[i]);
        scanf("%d",&n2);
        for(int i = 1;i <= n2;i++) scanf("%d",&num2[i]);
        memset(d,0,sizeof(d));
        for(int i = 1;i <= n1;i++)
        {
            int m = 0;
            for(int j = 1;j <= n2;j++)
            {
                if(num1[i] > num2[j] && d[j] > m) m = d[j];
                if(num1[i] == num2[j]) d[j] = m+1;
            }
        }
        int ans = 0;
        for(int i = 1;i <= n2;i++) ans = max(ans,d[i]);
        printf("%d\n",ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值