最长公共子序列 (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;
}