• 序列型动态规划总结


    原题来自维基OI

    最长上升子序列

    题目描述:

    序列 { 1,2,...,n } 的一个子序列是指序列 { i1, i2, ……, ik },其中 1<=i1 < i2 < …… < ik<=n, 序列 { a1, a2, ……, an } 的一个子序列是指序列 { ai1, ai2, ……, aik },其中 { i1, i2, ……, ik } 是 { 1, 2, ……, n } 的一个子序列.同时,称 k 为此子序列的长度.

    如果 { ai1, ai2, ……, aik } 满足 ai1 ≤ ai2 ≤ …… ≤ aik,则称之为上升子序列.如果不等号都是严格成立的,则称之为严格上升子序列.同理,如果前面不等关系全部取相反方向,则称之为下降子序列和严格下降子序列. 

    长度最长的上升子序列称为最长上升子序列.本问题对于给定的整数序列,请求出其最长严格上升子序列的长度.

    输入描述:第一行,一个整数N;第二行,N个整数(N <= 5000)

    输出描述:输出最长严格上升子序列(以下简称子序列)的长度

    题目分析:典型的动态规划题,用动态规划的方式分析吧,设读入的整数存在数组origin里面,长度为len,也就是输入中所说的整数N,用f数组来保存子序列的长度,f[i]表示以origin[i]为结尾的数组长度,考虑怎样求f[i],以f[i]为结尾的最长严格上升子序列,那么origin[i]一定比子序列中前一个大,换句话说,只要在origin[i]前面(origin[1]~origin[i-1])比它大的数再加上origin[i],就可以成为一个严格上升子序列,其长度为f[1~i-1]+1.重新捋一遍,只要origin[i]比origin[1]~origin[i-1]中的一个大,那么以它为结尾的严格上升子序列长度为f[1~i-1]+1,而最大长度就是其最大值.状态转移方程f[i] = max(f[j]) + 1;(1 <= j < i && origin[j] < origin[i])

    AC代码如下:

     1 #include <iostream>
     2 #include <algorithm>
     3 using namespace std;
     4 
     5 int main()
     6 {
     7     int origin[501], f[501], len;
     8     cin >> len;
     9     f[1] = 1;
    10     for (int i = 1; i <= len; ++i) cin >> origin[i];
    11     for (int i = 2; i <= len; ++i)
    12     {
    13         int m = 1;
    14         for (int j = 1; j < i; ++j)
    15         {
    16             if (origin[j] < origin[i])
    17                 m = max(m, f[j] + 1);
    18         }
    19         f[i] = m;
    20     }
    21     cout << *max_element(&f[1], &f[len+1]);
    22 }

    线段覆盖

    题目描述:数轴上有n条线段,线段的两端都是整数坐标,坐标范围在0~1000000,每条线段有一个价值,请从n条线段中挑出若干条线段,使得这些线段两两不覆盖(端点可以重合)且线段价值之和最大.n<=1000

    输入描述:第一行一个整数n,表示有多少条线段.接下来n行每行三个整数, ai bi ci,分别代表第i条线段的左端点ai,右端点bi(保证左端点<右端点)和价值ci.

    题目分析:设len储存线段条数,3个数组,left储存线段左端点坐标,right储存线段右端点坐标,f起初储存线段的价值(储存线段价值相当于初始化f),计算过程中f[i]表示以第i条线段为结尾的不覆盖线段集的最大价值.有两条不覆盖的线段,那么把它们放到一个集合里,价值为它们的价值和.考虑计算f[i],如果第i条线段不覆盖与1~i-1条线段中任意一条,那么f[i]就可以加入到以该线段为结尾的线段集中,则该线段集的价值为原线段集的价值+f[i],(自认为说的有些啰嗦),那么要求f[i]只要求f[1] ~ f[i-1]符合条件(符合条件就是不覆盖,即right[j] <= left[i])的最大值再加上f[i]即可,状态转移方程为f[i] = max(f[1] ~ f[i-1]) + f[i](right[j] <= left[i]).等等,你怎么知道线段就是从左到右放在数轴上的?所以要在开头对右端点排序.

    AC代码如下:

     1 #include <iostream>
     2 #include <algorithm>
     3 using namespace std;
     4 int Left[1001] = {1000001}, Right[1001] = {1000001}, f[1001] = {1000001}, len;
     5 void superswap(int i, int j)//每次交换是交换整条线段,所以要交换三个
     6 {
     7     swap(Left[i], Left[j]);
     8     swap(Right[i], Right[j]);
     9     swap(f[i], f[j]);
    10 }
    11 void selection_sort()//选择排序,按照右端点排序
    12 {
    13     for (int i = 1; i <= len; ++i)
    14     {
    15         int maxsub = 0;
    16         for (int j = i; j <= len; ++j)
    17         {
    18             if (Right[maxsub] > Right[j])
    19                 maxsub = j;
    20         }
    21         superswap(i, maxsub);
    22     }
    23 }
    24 int main()
    25 {
    26     cin >> len;
    27     for (int i = 1; i <= len; ++i)
    28         cin >> Left[i] >> Right[i] >> f[i];
    29     selection_sort();
    30     for (int i = 2; i <= len; ++i)
    31     {
    32         int maxm = f[i];
    33         for (int j = 1; j < i; ++j)
    34         {
    35             if (Right[j] <= Left[i])
    36                 maxm = max(maxm, f[j] + f[i]);
    37         }
    38         f[i] = maxm;
    39     }
    40     cout << *max_element(&f[1], &f[len + 1]);//求最大值
    41 }

    拦截导弹

    题目描述:某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度.某天,雷达捕捉到敌国的导弹来袭.由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹.

    输入描述:输入导弹依次飞来的高度(雷达给出的高度数据是不大于30000的正整数)

    输出描述:输出这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统.

    题目分析:暂时先不发,让我自己理一理思路.

    AC代码如下:

     1 #include <iostream>
     2 #include <algorithm>
     3 #include <cstdio>
     4 using namespace std;
     5 
     6 int main()
     7 {
     8     int height[55], len = 0, f[55];
     9     while (scanf("%d", &height[++len]) != EOF);
    10     f[1] = 1;
    11     for (int i = 2; i <= len; ++i)
    12     {
    13         int maxm = 1;
    14         for (int j = 1; j < i; ++j)
    15         {
    16             if (height[j] >= height[i])
    17                 maxm = max(maxm, f[j] + 1);
    18         }
    19         f[i] = maxm;
    20     }
    21     cout << *max_element(&f[1], &f[len]) << endl;
    22     for (int i = 2; i <= len; ++i)
    23     {
    24         int maxm = 1;
    25         for (int j = 1; j < i; ++j)
    26         {
    27             if (height[j] < height[i])
    28                 maxm = max(maxm, f[j] + 1);
    29         }
    30         f[i] = maxm;
    31     }
    32     cout << *max_element(&f[1], &f[len]) << endl;
    33     return 0;
    34 }

    非常抱歉,最后一题的分析我自己还说不出来,请允许我再理一理思路.如果有什么不合理之处,敬请批评指正.

  • 相关阅读:
    java接口对入参的判断校验
    sqlyog使用技巧
    mysql 数据库的表中复制一条数据并添加到该表中
    union all ,union 注意事项,查询结果集中的字段名称顺序必须一致
    IDEA连接mysq数据库,其实很简单
    git版本回退、git远程分支管理、git本地分支管理、git生产代码bug修复
    Vue上拉加载下拉刷新---vue-easyrefresh
    Flutter上拉加载下拉刷新---flutter_easyrefresh
    vue-cli webpack多Html页面的配置(附框架vue-webpack-multipage实例)
    Qt使用镜像源快速安装与更新
  • 原文地址:https://www.cnblogs.com/zkx06111/p/3280564.html
Copyright © 2020-2023  润新知