序列问题模型(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;
}
相关推荐
无尽的罚坐人生2 小时前
hot 100 35. 搜索插入位置
数据结构·算法·leetcode·二分查找
自信150413057592 小时前
数据结构之实现链式结构二叉树
c语言·数据结构·算法
EE工程师2 小时前
数据结构篇 - C语言如何实现OOP
数据结构·oop
Barkamin2 小时前
堆排序简单实现
java·数据结构·算法·排序算法
迈巴赫车主2 小时前
天梯赛 L2-004 这是二叉搜索树吗?java
java·开发语言·数据结构·算法·天梯赛
沐苏瑶3 小时前
Java 数据结构精讲:二叉树遍历算法与底层实现剖析
数据结构·算法
芸忻3 小时前
day 13 第六章 二叉树 part01代码随想录算法训练营71期
数据结构·算法
luckycoding4 小时前
1487. 保证文件名唯一
数据结构·算法·leetcode
_dindong4 小时前
【单调栈/队列&并查集&字符串哈希&Tire树】习题集锦
数据结构·c++·算法·哈希算法