IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 【动态规划】—— 最长上升子序列模型 -> 正文阅读

[数据结构与算法]【动态规划】—— 最长上升子序列模型


AcWing 1017. 怪盗基德的滑翔翼

输入样例:

3
8
300 207 155 299 298 170 158 65
8
65 158 170 298 299 155 207 300
10
2 1 3 4 5 6 7 8 9 10

输出样例:

6
6
9

当确定完方向起点完后,最长的距离是什么呢?

起点:a[i]

最长:以?a[i]?为起点的最长上升子序列 (LIS)


#include <iostream>
#include <algorithm>
using namespace std;

const int N = 110;

int n;
int a[N], f[N];

int main()
{
    int T;
    cin >> T;
    while(T -- )
    {
        cin >> n;
        for(int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
        
        // 正向求解 LIS
        int res = 0;
        for(int i = 1; i <= n; i ++ )
        {
            f[i] = 1;
            for(int j = 1; j < i; j ++ )
                if(a[i] > a[j])
                    f[i] = max(f[i], f[j] + 1);
            res = max(res, f[i]);
        }
        
        // 反向求解 LIS
        for(int i = n; i; i -- )
        {
            f[i] = 1;
            for(int j = n; j > i; j -- )
                if(a[i] > a[j])
                    f[i] = max(f[i], f[j] + 1);
            res = max(res, f[i]);
        }
        
        cout << res << endl;
    }
    return 0;
}

AcWing 1014. 登山?

输入样例:


输出样例:

4

题目中有以下条件

  1. 按照编号递增的顺序来浏览(子序列)
  2. 相邻的两个景点不能相同高度(严格单调)
  3. 一旦开始下降就不能上升?

目标:求最多能浏览多少景点

两个LIS然后求解出相加最大值


#include <iostream>
#include <algorithm>
using namespace std;

const int N = 1010;

int n;
int a[N];
int f[N], g[N];


int main()
{
    cin >> n;
    for(int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
    for(int i = 1; i <= n; i ++ )
    {
        f[i] = 1;
        for(int j = 1; j < i; j ++ )
            if(a[i] > a[j])
                f[i] = max(f[i], f[j] + 1);
    }
    
    for(int i = n; i; i -- )
    {
        g[i] = 1;
        for(int j = n; j > i; j -- )
            if(a[i] > a[j])
                g[i] = max(g[i], g[j] + 1);
    }
    
    int res = 0;
    for(int i = 1; i <= n; i ++ ) res = max(res, f[i] + g[i] - 1);
    
    cout << res << endl;
    
    return 0;
}

AcWing 1012. 友好城市

输入样例:

7
22 4
2 6
10 3
15 12
9 8
17 17
4 2

输出样例:

4

?所有合法的建桥方式都对应着一个因变量的上升子序列

?因此,该方案中,在上坐标排序的情况下,下坐标次序不是从小到大的,则必然不合法(会有相交)

于是,这题就变成了:桥以上坐标从小到大排序后,找出下坐标的最长上升子序列长度


#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 5010;

typedef pair<int, int> PII;

int n;
PII q[N];
int f[N];

int main()
{
    cin >> n;
    for(int i = 0; i < n; i ++ ) scanf("%d%d", &q[i].first, &q[i].second);
    sort(q, q + n);
    
    int res = 0;
    for(int i = 0; i < n; i ++ )
    {
        f[i] = 1;
        for(int j = 0; j < i; j ++ )
            if(q[i].second > q[j].second)
                f[i] = max(f[i], f[j] + 1);
        res = max(res, f[i]);
    }
    
    cout << res << endl;
    
    return 0;
}

AcWing 1016. 最大上升子序列和?

输入样例:

7
1 7 3 5 9 4 8

输出样例:

18


?

#include <iostream>
#include <algorithm>
using namespace std;

const int N = 1010;

int n;
int a[N], f[N];

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
    
    for(int i = 1; i <= n; i ++ )
    {
        f[i] = a[i];
        for(int j = 1; j < i; j ++)
            if(a[j] < a[i])
                f[i] = max(f[i], f[j] + a[i]);
    }
    int res = 0;
    for(int i = 1; i <= n; i ++ ) res = max(res, f[i]);
    cout << res << endl;
    return 0;
}

AcWing 1010. 拦截导弹?

输入样例:

389 207 155 300 299 170 158 65

输出样例:

6
2

这道题的第一问是一个经典的 LIS 问题

对于第二问,我们采用贪心思路

贪心流程:

从前往后扫描每个数,对于每个数:

? ? ? ? 情况1:如果现有的子序列的结尾都小于当前数,则创建新的子序列

? ? ? ? 情况2:将当前数放到大于等于它的最小的子序列后面

贪心证明:

1.如何证明两个数相等??A\geq B,A\leq B

?A 表示贪心算法得到的序列个数,B 表示最优解

?①?B\leq A,由最优解的性质可得

?②?A\leq B?调整法

? ? ? ??假设最优解对应的方案和当前方案不同

? ? ? ? 找到第一个不同数。

? ? ? ? 通过有限次的交换可以将最优解调整为贪心解,且不增加序列个数


#include <iostream>
#include <algorithm>
using namespace std;

const int N = 1010;

int n;
int q[N];
int f[N], g[N];

int main()
{
    while(cin >> q[n]) n ++ ;
    
    int res = 0;
    for(int i = 0; i < n; i ++ )
    {
        f[i] = 1;
        for(int j = 0; j < i; j ++ )
            if(q[j] >= q[i])
                f[i] = max(f[i], f[j] + 1);
        res = max(res, f[i]);
    }
    
    cout << res << endl;
    
    int cnt = 0;
    for(int i = 0; i < n; i ++ )
    {
        int k = 0;
        while(k < cnt && g[k] < q[i]) k ++ ;
        g[k] = q[i];
        if(k >= cnt) cnt ++ ;
    }
    
    cout << cnt << endl;
    
    return 0;
}

?AcWing 272. 最长公共上升子序列

4
2 2 1 3
2 1 2 3

输出样例:

2


这道题目是最长上升子序列最长公共子序列的结合版,在状态表示和状态计算上都是融合了这两道题目的方法。

状态表示:

f[i][j]?代表所有 a[1 ~ i] 和 b[i ~ j] 中以 b[j] 结尾的公共上升子序列的集合

f[i][j]的值等于该集合的子序列中长度的最大值?

状态计算:

首先根据公共子序列中是否包含a[i],将 f[i][j] 所代表的集合分成两个不重不漏的子集

  • 不包含 a[i] 的子集,最大值是 f[i - 1][j]
  • 包含a[i] 的子集,将这个子集继续划分,依据是子序列的倒数第二个元素在 b[] 中是哪个数
    • 子序列只包含 b[j] 一个数,长度是1
    • 子序列的倒数第二个数是 b[1] 的集合,最大长度是 f[i-1][1] + 1
    • ....
    • 子序列的倒数第二个数是 b[j - 1] 的集合,最大长度是 f[i-1][j-1] + 1

得到以下O(n^3)?的做法

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 3010;

int n;
int a[N], b[N];
int f[N][N];

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
    for(int i = 1; i <= n; i ++ ) scanf("%d", &b[i]);
    
    for(int i = 1; i <= n; i ++ )
        for(int j = 1; j <= n; j ++ )
        {
            f[i][j] = f[i - 1][j];
            if(a[i] == b[j])
            {
                f[i][j] = max(f[i][j], 1);
                for(int k = 1; k < j; k ++ )
                    if(b[k] < b[j])
                        f[i][j] = max(f[i][j], f[i][k] + 1);
            }
        }
        
    int res = 0;
    for(int i = 1; i <= n; i ++ ) res = max(res, f[n][i]);
    
    cout << res << endl;
    
    return 0;
}

然后我们发现每次循环求得的maxv是满足a[i] > b[k]的f[i - 1][k] + 1的前缀最大值。
因此可以直接将maxv提到第一层循环外面,减少重复计算,此时只剩下两重循环。

最终答案枚举子序列结尾取最大值即可。

#include <cstdio>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 3010;

int n;
int a[N], b[N];
int f[N][N];

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

    for (int i = 1; i <= n; i ++ )
    {
        int maxv = 1;
        for (int j = 1; j <= n; j ++ )
        {
            f[i][j] = f[i - 1][j];
            if (a[i] == b[j]) f[i][j] = max(f[i][j], maxv);
            if (a[i] > b[j]) maxv = max(maxv, f[i - 1][j] + 1);
        }
    }

    int res = 0;
    for (int i = 1; i <= n; i ++ ) res = max(res, f[n][i]);
    printf("%d\n", res);

    return 0;
}
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-07-21 21:45:58  更:2022-07-21 21:47:23 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 23:24:02-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码