AcWing
  • 首页
  • 课程
  • 题库
  • 更多
    • 竞赛
    • 题解
    • 分享
    • 问答
    • 应用
    • 校园
  • 关闭
    历史记录
    清除记录
    猜你想搜
    AcWing热点
  • App
  • 登录/注册

基础算法笔记

作者: 作者的头像   马丁 ,  2019-10-05 21:36:47 ,  所有人可见 ,  阅读 1309


5


1

LIS和LCS问题

例题1:最长上升子序列

题目描述

给定一个长度为N的数列,求数值严格单调递增的子序列的长度最长是多少。

输入格式

第一行包含整数N。

第二行包含N个整数,表示完整序列。

输出格式

输出一个整数,表示最大长度。

数据范围

1≤N≤10001≤N≤1000,

-10^9 ≤数列中的数≤ 10^9

样例

输入样例:
7
3 1 2 1 8 5 6
输出:
4

(动态规划) O(n^2)

以集合的方式理解DP:最主要的就是状态表示与状态计算

状态表示——集合:表示以第i个数结尾的上升子序列

    ——属性:存储的为最大值(一般为最大值、最小值、数量这三个任意一个)

f[i]就表示为第i个数结尾的最大的上升子序列的个数

状态计算:要求f[i],你就要知道前i - 1 个数有多少个数比a[i]小,如果比a[i]小那么f[i]的结果就要+1,于是得到转移方程为:f[i] = max(f[i],f[j] + 1)

时间复杂度

C++ 代码

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
int f[N],a[N],n;
int main(){
    cin >> n;
    for(int i = 1; i <= n; i ++) cin >> a[i];

    for(int i = 1; i <= n; i ++){
        f[i] = 1;                   //表示只选第i个
        for(int j =1 ; j < i; j ++)     //从 j 到 i -1 枚举
            if(a[i] > a[j]) f[i] = max(f[i], f[j] + 1); //如果满足前面的数比 a[i] 小就更新f[i]
    }

    int res = 0;
    for(int i = 0; i<=n; i ++) res = max(res,f[i]);//找出最大值
    cout << res;
    return 0;
}

例题1:最长上升子序列

题目描述

给定两个长度分别为N和M的字符串A和B,求既是A的子序列又是B的子序列的字符串长度最长是多少。

输入格式

第一行包含两个整数N和M。

第二行包含一个长度为N的字符串,表示字符串A。

第三行包含一个长度为M的字符串,表示字符串B。

字符串均由小写字母构成。

输出格式

输出一个整数,表示最大长度。

数据范围

1≤N≤1000

样例

输入:
4 5
acbd
abedc
输出:
3

算法1

(暴力枚举) O(n^2)

这里的状态f[i,j]表示所有在第一个序列中的前i个字母中出现,且在第二个序列的前j个字母中出现的子序列。

——属性为:max

这里难点在于状态计算的划分区间,我们通过选与不选将它分成4种情况:

00表示a[i],b[j]都不选:显然这种情况很好计算,就是f[i-1,j-1]的值,因为新增a[i],b[j]都没用

01表示不选a[i]选a[j]:f[i-1,j]

10表示选a[i]不选a[j]:f[i,j-1]

11表示a[i],b[j]都选:它的计算是f[i-1,j -1] + 1 ,表示在之前的基础之上在加一

难点是第二种和第三种情况:因为f[i - 1,j]和第二种情况并不相等,第二种情况一定是f[i - 1,j]的子集,而f[i - 1,j]又是f[i,j]的子集,所以我们才可以使用f[i-1,j]来代替第二种情况的取值,第三种情况类似,不过再写的时候一般都会忽略第一种情况,因为第一种情况又是第二种和第三种情况的子集~~~

C++ 代码

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
int f[N][N];
char a[N],b[N];
int n,m;
int main(){
    cin >> n >> m;
    scanf("%s%s",a + 1, b + 1); // a+1 ,b+1 表示读入从1号下标开始

    for(int i = 1; i <= n; i ++){   //枚举a[i]
        for(int j = 1; j <= m; j++){//枚举b[j]
            f[i][j] = max(f[i - 1][j],f[i][j - 1]);//表示 00 01 10的情况
            if(a[i] == b[j]) f[i][j] = max(f[i][j],f[i-1][j-1] + 1);//当a[i] == b[j] 表示 11这种情况
        }
    }
    cout << f[n][m];        //结果
    return 0;
}

混一下经验,后面将大佬将的背包问题整理出来

0 评论

App 内打开
你确定删除吗?
1024
x

© 2018-2025 AcWing 版权所有  |  京ICP备2021015969号-2
用户协议  |  隐私政策  |  常见问题  |  联系我们
AcWing
请输入登录信息
更多登录方式: 微信图标 qq图标 qq图标
请输入绑定的邮箱地址
请输入注册信息