• 动态规划4.2——子序列问题


    题目上添加了超链接,大家点一下题目就会自动跳转到Poj原题界面~~              冲鸭冲鸭ヾ(◍°∇°◍)ノ゙。

    前言:

    视频讲解链接:https://www.bilibili.com/video/BV1i44y1x7wT?spm_id_from=333.999.0.0

    建议大家按随笔顺序阅览,序列型动态规划是常见动态规划类型当中的重点, 占常见动态规划类型里的20%,这里给出的几道题都很经典,大家可以都当作板子抄下来。

    动态规划组成部分:

    1:确定状态

          —确定最后一步(最优策略)

          —抽象子问题

    2:归纳转移方程

    3:初始条件和边界情况

    4:计算顺序

     

    4.2.1 Longest Ordered Subsequence (2533)

    题意:在一个给定的数值序列中,找到一个子序列,使得这个子序列元素的数值依次递增,并且这个子序列的长度尽可能地大,子序列中的元素在原序列中不一定是连续的。

    小笔记:最长递增子序列

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    const int N = 1001;
    int main()
    {
        int n;
        scanf("%d", &n);
        int ans = 1;
        int D[N];
        for (int i = 0; i < n; i++)
        {
            int a[N];
            scanf("%d", &a[i]);
            D[i] = 1;
            for (int j = 0; j < i; j++)
                if (a[j] < a[i]) // * 就这儿!
                    D[i] = max(D[i], D[j] + 1);
            ans = max(ans, D[i]);
        }
        printf("%d
    ", ans);
        return 0;
    }
    

      以上代码求的是最长递增子序列长度,其他情况修改代码中*的位置:

    ①最长不降子序列长度,改成a[j] ≤ a[i];

    ②最长递减子序列长度,改成a[j] > a[i];

    ③最长不升子序列长度,改成a[j] ≥ a[i];

     

    4.2.2 Wooden Sticks (1065)

    题意:有n个木棍,已知长度和重量,将木棍放在机器上加工。机器的设置时间

    ①第一个木棍需要1分钟;

    ②处理的木棍长度为l,重量为w,如果下一个木棍长度l’≤l并且w’≤w,不需要设置时间,否则需要1分钟。

    计算处理n个木棍的最少设置时间。

    小笔记:最长不上升子序列、偏序、这题也可以贪心解

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    const int N = 5001;
    int main()
    {
        int t;
        scanf("%d", &t);
        while (t--)
        {
            int n;
            scanf("%d", &n);
            pair<int, int> a[N];
            for (int i = 0; i < n; i++)
                scanf("%d%d", &a[i].first, &a[i].second);
            sort(a, a + n);
            //通过二分查找方法实现最长递减子序列的值
            int k = 0;
            int d[N];
            for (int i = 0; i < n; i++)
            {
                int p = -1;
                int r = k;
                while (r - p > 1)
                {
                    int mid = (p + r) / 2;
                    if (d[mid] > a[i].second)
                        p = mid;
                    else
                        r = mid;
                }
                d[r] = a[i].second; //将已经搜索到的子序列保存到d中
                if (r == k)
                    k++;
            }
            printf("%d
    ", k);
        }
        return 0;
    }
    

      

    4.2.3 Alignment (1836)

    题意:n个士兵站成一行,队列中任何士兵的某一侧(左侧或者右侧)没有身高大于或者等于他的士兵,他就可以看到队列这一侧的最外面的位置。为了使队列中的每一个士兵都可以看到他某一侧的最外面位置,问最少出列多少士兵可以使剩下的士兵满足这个条件。

    小笔记:最长递增子序列

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    const int N = 1005;
    int main()
    {
        double a[N];
        int n;
        scanf("%d", &n);
        int L[N], R[N];
        for (int i = 0; i < n; i++)
        {
            scanf("%lf", &a[i]);
            L[i] = 1;
            for (int j = 0; j < i; j++)
                if (a[j] < a[i])
                    L[i] = max(L[i], L[j] + 1);
        }
        for (int i = n - 1; i >= 0; i--)
        {
            R[i] = 1;
            for (int j = n - 1; j > i; j--)
                if (a[j] < a[i])
                    R[i] = max(R[i], R[j] + 1);
        }
        int ans = 1;
        for (int i = 0; i < n; i++)
            for (int j = i + 1; j < n; j++)
                ans = max(ans, L[i] + R[j]);
        printf("%d
    ", n - ans);
        return 0;
    }
    

      

    4.2.4 Maximum sum (2479)

    题意:n个整数构成的数列a[1…n],求d(A)。

    小笔记:最大连续子序列和

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    const int N = 50005;
    const int MIN = -10000;
    int main()
    {
        int t;
        scanf("%d", &t);
        while (t--)
        {
            int n;
            scanf("%d", &n);
            int L[N], R[N];
            L[0] = R[n + 1] = MIN;
            int a[N];
            for (int i = 0; i < n; i++)
            {
                scanf("%d", &a[i]);
                L[i + 1] = max(a[i], L[i] + a[i]);
            }
            for (int i = 1; i <= n; i++)
                L[i] = max(L[i - 1], L[i]);
            for (int i = n; i > 0; i--)
                R[i] = max(a[i - 1], R[i + 1] + a[i - 1]);
            for (int i = n; i > 0; i--)
                R[i] = max(R[i + 1], R[i]);
            int ans = MIN;
            for (int i = 1; i < n; i++)
                ans = max(ans, L[i] + R[i + 1]);
            printf("%d
    ", ans);
        }
        return 0;
    }
    

      

    4.2.5To the Max (1050)

    题意:一个二维矩阵包含正数和负数,任何连续的矩阵空间构成它的子矩阵,矩阵中所有元素的和称为矩阵的和,求最大的子矩阵和。

    小笔记:进阶,最大子矩阵和。按照合适的顺序将求子矩阵的和的计算转化成求子序列的和,在求的过程中记录最大值。

    题解:     ①从左到右计算每一行,将Σa[i,1…j]保存到a[i,j]中,这样,任意一段Σa[i,k…j]就可以通过a[i,j]-a[i,k]得到;

    ②先取1列,计算由1列组成的最大子矩阵和,因为只有1列,相当于求最大的连续子序列和。这里与上一个问题求最大连续子序列和不同,本题的连续子序列必须包含i点,而上题不需要。

    ③扩展到多列的情况,因为k…j列可以由a[i,j]-a[i,k]得出,依旧是1个值,计算方法和②一样;

    ④最后扩展到所有列,就找到了最大子矩阵和。

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    int main()
    {
        int ans = -128;
        int a[101][101] = {0};
        int n;
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
            {
                int t;
                scanf("%d", &t);
                a[i][j] = a[i][j - 1] + t;
            }
        for (int j = 1; j <= n; j++)
            for (int k = 0; k < j; k++)
                for (int i = 1, sum = 0; i <= n; i++)
                {
                    sum = a[i][j] - a[i][k] + max(0, sum);
                    ans = max(ans, sum);
                }
        printf("%d
    ", ans);
        return 0;
    }
    

      

     
  • 相关阅读:
    ThinkPHP框架被爆任意代码执行漏洞
    thinkphp session 跨域问题解决方案
    网摘地址
    thinkphp的各种内部函数 D()、F()、S()、C()、L()、A()、I()详解
    IP相关(近两天的学习总结)
    ThinkPHP 手册摘录之(跨模块)调用
    c#让程序在WIN7下兼容模式运行
    QT学习之经典控件源码(如此强大)
    C#自定义控件七水波纹
    C#自定义控件五报警按钮
  • 原文地址:https://www.cnblogs.com/thx2199/p/15116727.html
Copyright © 2020-2023  润新知