HDU 1423 Greatest Common Increasing Subsequence(最长公共上升LCIS)

本文介绍了解决最长公共上升子序列(LCIS)问题的方法,通过动态规划求解两个数字序列的最长公共严格递增子序列,并提供了一种高效的时间复杂度算法实现。此外还探讨了如何输出一个LCIS及字典序最小的LCIS。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

HDU 1423 Greatest Common Increasing Subsequence(最长公共上升LCIS)

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

题意:

       给你两个数字组成的串a和b,要你求出它们的最长公共严格递增子序列的长度(LCIS).

分析:

       首先我们令f[i][j]==x表示是a串的前i个字符与b串的前j个字符构成的且以b[j]结尾的LCIS长度.

       当a[i]!=b[j]时:

       f[i][j]=f[i-1][j]

       当a[i]==b[j]时:

       f[i][j]=max(f[i-1][k])+1. 其中 k<j且b[k]<b[j].

       如果我们按上述递推公式依次枚举i, j, k 的话,那么时间复杂度就是O(n*m^2)了.

       其实我们只要枚举i, j. 然后我们记录当前的最大f[i-1][k]值即可(要满足k<jb[k]<b[j]). 程序实现用到了一个技巧, 即枚举(i, j)情况时假设a[i]的值与b[j+1]的值是相等的. 那么只要b[j]<a[i]的话, 我们直接更新max=f[i-1][j]即可. 如果下一轮a[i]==b[j+1], 那么上一轮max保存的值f[i-1][j] 可以肯定j<j+1 且b[j]<a[i]==b[j+1]. (b[j]变成b[k]也是一样)

       如何输出一个LCIS串呢?

      我们首先找到使得f[n][id]取最大值的id. 然后它肯定是由f[n-1][k](k<idb[k]<b[id]) 构成的. 所以我们只需要往前找到那个f[n-1][k]==f[n][id]-1 且 b[k]<b[id]的值逆序输出即可. 其实动态规划的所有输出方案的问题都可以这么输出.

       如果想输出字典序最小的LCIS串呢?

我们只需要将原来的两个序列逆转,然后找出最长公共递减子序列. 然后从第一个LCDS的字符开始找尽可能字典序小的字符即可. 其实思想大致都是一样的.

AC代码:

</pre><pre name="code" class="cpp">#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=500+5;

int n;//a串长
int m;//b串长
int a[maxn];//a串
int b[maxn];//b串
int f[maxn][maxn];

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        scanf("%d",&m);
        for(int i=1;i<=m;i++)
            scanf("%d",&b[i]);

        memset(f,0,sizeof(f));
        for(int i=1;i<=n;i++)
        {
            int max=0;//当前f[i-1][k]最大值且 k<j&&b[k]<b[j]
            int flag=-1;
            for(int j=1;j<=m;j++)
            {
                f[i][j]=f[i-1][j];
                if(a[i]>b[j] && max<f[i-1][j])
                {
                    max=f[i-1][j];
                    flag=j;
                }
                if(a[i]==b[j])
                {
                    f[i][j]=max+1;
                }
            }
        }

        int max_val=0;
        int id=-1;
        for(int i=1;i<=m;i++)
        {
            if(max_val<f[n][i])
            {
                max_val=f[n][i];
                id=i;
            }
        }

        printf("%d\n",max_val);
        if(T) printf("\n");

        //逆序输出一个LCIS串
        /*
        int i=n;
        while(id!=-1 && f[i][id]>=1)
        {
            printf("%d ",b[id]);
            int tmp=f[i][id];
            int tmp_v=b[id];
            //往前找到合法的f[i-1][k]
            while(id!=0)
            {
                id--;
                if(f[i-1][id]==tmp-1 && b[id]<tmp_v)
                    break;
            }
            i--;
        }
        printf("\n");
        */

    }
    return 0;
}



### HDU 1159 最长公共子序列 (LCS) 解题思路 #### 动态规划状态定义 对于两个字符串 `X` 和 `Y`,长度分别为 `n` 和 `m`。设 `dp[i][j]` 表示 `X[0...i-1]` 和 `Y[0...j-1]` 的最长公共子序列的长度。 当比较到第 `i` 个字符和第 `j` 个字符时: - 如果 `X[i-1]==Y[j-1]`,那么这两个字符可以加入之前的 LCS 中,则有 `dp[i][j]=dp[i-1][j-1]+1`[^3]。 - 否则,如果 `X[i-1]!=Y[j-1]`,那么需要考虑两种情况中的最大值:即舍弃 `X[i-1]` 或者舍弃 `Y[j-1]`,因此取两者较大者作为新的 LCS 长度,即 `dp[i][j]=max(dp[i-1][j], dp[i][j-1])`。 时间复杂度为 O(n*m),其中 n 是第一个字符串的长度而 m 是第二个字符串的长度。 #### 实现代码 以下是 Python 版本的具体实现方式: ```python def lcs_length(X, Y): # 初始化二维数组用于存储中间结果 m = len(X) n = len(Y) # 创建(m+1)x(n+1)大小的表格来保存子问题的结果 dp = [[0]*(n+1) for _ in range(m+1)] # 填充表项 for i in range(1, m+1): for j in range(1, n+1): if X[i-1] == Y[j-1]: dp[i][j] = dp[i-1][j-1] + 1 else: dp[i][j] = max(dp[i-1][j], dp[i][j-1]) return dp[m][n] # 测试数据输入部分可以根据具体题目调整 if __name__ == "__main__": while True: try: a = input().strip() b = input().strip() result = lcs_length(a,b) print(result) except EOFError: break ``` 此程序会读入多组测试案例直到遇到文件结束符(EOF)。每组案例由两行组成,分别代表要计算其 LCS 的两个字符串。最后输出的是它们之间最长公共子序列的长度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值