0
点赞
收藏
分享

微信扫一扫

最长上升子序列模型

草原小黄河 2022-03-26 阅读 65

模板题:
AcWing 1017. 怪盗基德的滑翔翼(做两次二分的优化就行)
AcWing 1014. 登山
f [ i ] [ 0 ] 表 示 到 第 i 个 点 , 一 直 上 升 的 最 大 长 度 f[i][0]表示到第i个点,一直上升的最大长度 f[i][0]i
f [ i ] [ 1 ] 表 示 到 第 i 个 点 , 有 下 降 段 的 最 大 长 度 f[i][1]表示到第i个点,有下降段的最大长度 f[i][1]i
注意 f [ i ] [ j ] f[i][j] f[i][j]的转移方程,$ f [ i ] [ 1 ] f[i][1] f[i][1]的转移要对 f [ j ] [ 0 ] 和 f [ j ] [ 1 ] 都 要 取 m a x f[j][0]和f[j][1]都要取max f[j][0]f[j][1]max
核心代码如下:

int main()
{
    int res = -1;
    cin >> N;
    for (int i = 1; i <= N; ++i)
    {
        cin >> h[i];
        f[i][0] = f[i][1] = 1;
        for (int j = 1; j < i; ++j)
        {
            if (h[j] < h[i])f[i][0] = max(f[i][0], f[j][0] + 1);
            if (h[j] > h[i])f[i][1] = max({f[i][1], f[j][0] + 1, f[j][1] + 1});
        }
        res = max({res, f[i][0], f[i][1]});
    }
    cout << res;
    return 0;
}

AcWing 482. 合唱队形 这道题和上道题一模一样
AcWing 1012. 友好城市 这道题先排序,再求最长上升子序列
AcWing 1016. 最大上升子序列和 暴力直接做,维护一个dp
AcWing 1010. 拦截导弹维护两个序列就行,一个维护最长上升子序列,另一个维护个数(这个序列是单调递增的),用二分做

AcWing 187. 导弹防御系统 这道题有点难感觉,具体来说就是迭代加深,然后把每个数分别讨论放在上升子序列和下降子序列中。迭代加深模板如下:

{
	depth = 0;
	while (!DFS(1, 0, 0)) ++depth;
	cout << depth << '\n';
}

整道题代码如下:

#include <iostream>

using namespace std;

const int N = 55;
int n, a[N], up[N], down[N], depth;

bool DFS(int u, int sum_up, int sum_down)
{
    if (sum_up + sum_down > depth)
        return false;
    if (u == n + 1)
        return true;

    int k = 0;
    while (k < sum_up && up[k] >= a[u])
        ++k;
    int t = up[k];
    up[k] = a[u];
    if (k == sum_up && DFS(u + 1, sum_up + 1, sum_down))//需要新开
        return true;
    else if (k < sum_up && DFS(u + 1, sum_up, sum_down))//不需要新开
        return true;
    up[k] = t;


    k = 0;
    while (k < sum_down && down[k] <= a[u])
        ++k;
    t = down[k];
    down[k] = a[u];
    if (k == sum_down && DFS(u + 1, sum_up, sum_down + 1))//需要新开
        return true;
    else if (k < sum_down && DFS(u + 1, sum_up, sum_down))
        return true;
    down[k] = t;

    return false;
}


int main()
{
    while (cin >> n && n)
    {
        for (int i = 1 ; i <= n ; ++i)cin >> a[i];

        depth = 0;
        while (!DFS(1, 0, 0)) ++depth;

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

AcWing 272. 最长公共上升子序列
f [ i ] [ j ] f[i][j] f[i][j]表示 A [ 1 − i ] A[1-i] A[1i] B [ 1 − j ] B[1- j ] B[1j]中以 b [ j ] b[j] b[j]结尾的最长公共上升子序列的最大长度,将集合分为不存在 a [ i ] a[i] a[i]和存在 a [ i ] a[i] a[i]的两个集合
前者是 f [ i ] [ j ] = f [ i − 1 ] [ j ] f[i][j]=f[i-1][j] f[i][j]=f[i1][j]
后者的集合又可以分为倒数第二个数分别是 b [ 1 ] , b [ 2 ] ⋅ ⋅ ⋅ b [ j − 1 ] b[1],b[2]···b[j-1] b[1],b[2]b[j1]结尾的最长公共上升子序列的长度
下面这是优化前的版本,会 T L E TLE TLE

for(int i = 1; i <= N; ++i)
    {
        for(int j = 1; j <= N; ++j)
        {
            f[i][j] = f[i - 1][j];//a[i]不包含在里面

            //下面是a[i]包含在里面
            if(A[i] == B[j])
            {
                int maxv = 1;
                for(int k = 1; k < j; ++k)
                {
                    if(B[k] < B[j])
                        maxv = max(maxv, f[i - 1][k] + 1);
                }

                f[i][j] = max(f[i][j], maxv);
            }
        }
    }

作者:北极汪汪熊
链接:https://www.acwing.com/activity/content/code/content/2060385/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

下面是优化后的版本,因为 m a x v maxv maxv是一个前缀最大值
‘ 在 这 里 插 入 代 码 片 ‘ `在这里插入代码片`

for(int i = 1; i <= N; ++i)
    {
        int maxv=1;
        for(int j = 1; j <= N; ++j)
        {
            f[i][j] = f[i - 1][j];//a[i]不包含在里面

            if(A[i]>B[j])
                maxv=max(maxv,f[i-1][j]+1);

            //下面是a[i]包含在里面
            if(A[i] == B[j])
                f[i][j]=max(f[i][j],maxv);
        }
    }

作者:北极汪汪熊
链接:https://www.acwing.com/activity/content/code/content/2060385/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
举报

相关推荐

0 条评论