序列问题模型(LIS LCS LCIS)

最长公共子序列 (LCS) 问题

cs 复制代码
#include <stdio.h>

// 定义最大长度,根据题目 N, M <= 1000
#define MAXN 1005

int a[MAXN];
int b[MAXN];
int dp[MAXN][MAXN];

// 辅助函数:返回两个整数中的较大值
int max(int x, int y) {
    return x > y ? x : y;
}

int main() {
    int N, M;
    
    // 1. 读取输入
    if (scanf("%d %d", &N, &M) != 2) {
        return 0; 
    }

    for (int i = 0; i < N; i++) {
        scanf("%d", &a[i]);
    }

    for (int i = 0; i < M; i++) {
        scanf("%d", &b[i]);
    }

    // 2. 动态规划过程
    // i 代表数组 a 的前 i 个元素 (1 到 N)
    // j 代表数组 b 的前 j 个元素 (1 到 M)
    for (int i = 1; i <= N; i++) {
        for (int j = 1; j <= M; j++) {
            // 注意:数组下标是从 0 开始的,所以比较的是 a[i-1] 和 b[j-1]
            if (a[i-1] == b[j-1]) {
                // 如果元素相同,长度 = 左上角的值 + 1
                dp[i][j] = dp[i-1][j-1] + 1;
            } else {
                // 如果元素不同,长度 = 上方值 和 左方值 中的较大者
                dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
            }
        }
    }

    // 3. 输出结果
    printf("%d\n", dp[N][M]);

    return 0;
}

最长上升子序列(LIS)问题

cs 复制代码
#include <stdio.h>

#define MAXN 1005 // 根据题目 N <= 1000 定义最大数组大小

int a[MAXN];      // 存储对手的战力值
int dp[MAXN];     // dp[i] 表示以 a[i] 结尾的最长上升子序列长度

// 辅助函数:返回两个整数中的较大值
int max(int x, int y) {
    return x > y ? x : y;
}

int main() {
    int N;
    
    // 1. 读取输入
    if (scanf("%d", &N) != 1) {
        return 0;
    }

    for (int i = 0; i < N; i++) {
        scanf("%d", &a[i]);
        dp[i] = 1; // 初始化:每个元素自身构成一个长度为1的子序列
    }

    // 2. 动态规划过程
    // 外层循环 i 从 1 到 N-1,计算以 a[i] 结尾的LIS长度
    for (int i = 1; i < N; i++) {
        // 内层循环 j 从 0 到 i-1,寻找可以接在 a[j] 后面的情况
        for (int j = 0; j < i; j++) {
            if (a[j] < a[i]) { 
                // 如果 a[j] < a[i],则 a[i] 可以接在 a[j] 后面
                dp[i] = max(dp[i], dp[j] + 1);
            }
        }
    }

    // 3. 找出 dp 数组中的最大值,即为最终答案
    int ans = 0;
    for (int i = 0; i < N; i++) {
        ans = max(ans, dp[i]);
    }

    // 4. 输出结果
    printf("%d\n", ans);

    return 0;
}

最长公共上升子序列(LCIS)问题

cs 复制代码
#include <stdio.h>

#define MAXN 105 // 根据题目 N, M <= 100 定义

int a[MAXN];
int b[MAXN];
int dp[MAXN][MAXN]; // dp[i][j] 表示 a[0..i-1] 和 b[0..j-1] 中,以 b[j-1] 结尾的LCIS长度

// 辅助函数:返回两个整数中的较大值
int max(int x, int y) {
    return x > y ? x : y;
}

int main() {
    int N, M;
    
    // 1. 读取输入
    if (scanf("%d %d", &N, &M) != 2) {
        return 0;
    }

    for (int i = 0; i < N; i++) {
        scanf("%d", &a[i]);
    }

    for (int i = 0; i < M; i++) {
        scanf("%d", &b[i]);
    }

    // 2. 动态规划过程
    for (int i = 1; i <= N; i++) {
        int max_val = 0; // 用于记录 max(dp[i-1][k]),其中 b[k-1] < a[i-1]
        for (int j = 1; j <= M; j++) {
            // 默认情况下,dp[i][j] 继承自 dp[i-1][j]
            dp[i][j] = dp[i-1][j];
            
            // 如果 b[j-1] < a[i-1],更新 max_val
            // 这表示 b[j-1] 可能成为未来匹配 a[i-1] 的那个序列的前一个元素
            if (b[j-1] < a[i-1]) {
                if (dp[i-1][j] > max_val) {
                    max_val = dp[i-1][j];
                }
            }
            // 如果 b[j-1] == a[i-1],说明找到一个公共元素
            // 我们可以将它接在之前记录的、以小于 a[i-1] 的值结尾的最长序列后面
            else if (b[j-1] == a[i-1]) {
                dp[i][j] = max_val + 1;
            }
        }
    }

    // 3. 找出 dp 表中的最大值作为答案
    int ans = 0;
    for (int i = 1; i <= N; i++) {
        for (int j = 1; j <= M; j++) {
            if (dp[i][j] > ans) {
                ans = dp[i][j];
            }
        }
    }

    // 4. 输出结果
    printf("%d\n", ans);

    return 0;
}

LIS 贪心 + 二分查找(不会超时)

cs 复制代码
#include <stdio.h>
#include <stdlib.h>

#define MAXN 300005

int a[MAXN];
int tails[MAXN]; // tails[i] 表示长度为 i+1 的上升子序列的最小末尾元素

// 二分查找:找到第一个 >= x 的位置
int lower_bound(int arr[], int left, int right, int x) {
    while (left < right) {
        int mid = (left + right) / 2;
        if (arr[mid] < x) {
            left = mid + 1;
        } else {
            right = mid;
        }
    }
    return left;
}

int main() {
    int n;
    scanf("%d", &n);

    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
    }

    int len = 0; // 当前最长上升子序列的长度

    for (int i = 0; i < n; i++) {
        // 在 tails[0...len-1] 中找第一个 >= a[i] 的位置
        int pos = lower_bound(tails, 0, len, a[i]);

        // 如果没找到(即所有元素都 < a[i]),则扩展长度
        if (pos == len) {
            tails[len++] = a[i];
        } else {
            // 否则替换该位置的值,使相同长度的序列末尾更小
            tails[pos] = a[i];
        }
    }

    printf("%d\n", len);
    return 0;
}

LIS变种问题

cs 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define N 100010
int a[N]; // 记录原数组
// f(i):截止a[i]的最长上升子序列的长度
// g(i):以a[i]为开头的最长上升子序列的长度
int f[N], g[N];
int n;

// 实现 max 函数(C语言没有内置的int型max)
int max(int a, int b) {
    return a > b ? a : b;
}

int main(int argc, char *argv[]) {
    int i, j; // 提前声明循环变量
    scanf("%d", &n); // 读取数组长度
    
    // 初始化数组和f、g数组
    for (i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        f[i] = 1;
        g[i] = 1;
    }

    // 计算 f 数组:截止a[i]的最长上升子序列长度
    for (i = 1; i <= n; i++) {
        for (j = 1; j < i; j++) {
            if (a[j] < a[i]) {
                f[i] = max(f[i], f[j] + 1);
            }
        }
    }
    
    // 计算 g 数组:以a[i]开头的最长上升子序列长度
    for (i = n; i >= 1; i--) {
        for (j = n; j > i; j--) {
            if (a[j] > a[i]) {
                g[i] = max(g[i], g[j] + 1);
            }
        }
    }

    // 遍历所有情况计算答案
    int ans = 1;
    for (i = 0; i <= n; i++) {
        for (j = i + 2; j <= n; j++) {
            if (a[j] == a[i] + 1) {
                ans = max(ans, f[i] + g[j]);  // 不能插入其它数字
            } else if (a[j] > a[i] + 1) {
                ans = max(ans, f[i] + g[j] + 1); // 可以插入其他数字
            }
            if (a[j] > 0) {
                ans = max(ans, g[j] + 1); // 左边插入一个更小的数字
            }
        }
        if (i < n) {
            ans = max(ans, f[i] + 1); // 右边插入一个更大的数字
        }
    }

    printf("%d\n", ans); // 输出结果
    return 0;
}
相关推荐
CSharp精选营1 天前
关系型 vs 非关系型:从原理到选型,一文搞定数据库核心分类
数据结构·nosql·关系型数据库·非关系型数据库·技术选型
刘马想放假5 天前
Modbus 全栈技术解析:TCP、RTU、ASCII、RTU over TCP
数据结构·网络协议
北域码匠6 天前
冒泡排序太慢?鸡尾酒排序双向优化,原生 C# 零第三方库完整代码
数据结构·排序算法·泛型·c# 算法·鸡尾酒排序·原生 c# 开发·冒泡排序优化·嵌入式算法
Darling噜啦啦12 天前
列表转树算法深度解析:从 Map 到 Reduce 的两种实现,面试高频考点
数据结构·算法·面试
小小工匠13 天前
Redis - 事务机制:能实现 ACID 属性吗
数据结构·redis·性能优化·并发·持久化
玖玥拾13 天前
C/C++ 数据结构(七)栈、容器适配器
c语言·数据结构·c++··容器适配器
Qres82114 天前
算法复键——树状数组
数据结构·算法
牛油果子哥q14 天前
并查集(DSU)超精讲,路径压缩、按秩合并、万能模板、连通性判定、最小生成树与刷题实战全解
数据结构·c++·最小生成树·并查集
凌波粒14 天前
LeetCode--491.递增子序列(回溯算法)
数据结构·算法·leetcode
WL学习笔记14 天前
单项不带头不循环链表
数据结构·链表