zoukankan      html  css  js  c++  java
  • NOIP200407合唱队形+最长上升子序列O(n^2)详解

    合唱队形解题报告

    2016-05-12   430——645

    NOIP200407合唱队形

    难度级别:A; 运行时间限制:1000ms; 运行空间限制:256000KB; 代码长度限制:2000000B

    试题描述

     N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形。
        合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2…,K,他们的身高分别为T1,T2,…,TK,则他们的身高满足T1<...<Ti>Ti+1>…>TK(1<=i<=K)。
        你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。

    输入

    第一行是一个整数N(2<=N<=100),表示同学的总数。第一行有n个整数,用空格分隔,第i个整数Ti(130<=Ti<=230)是第i位同学的身高(厘米)。

    输出

    包括一行,这一行只包含一个整数,就是最少需要几位同学出列。

    输入示例

    8
    186 186 150 200 160 130 197 220

    输出示例

    4

    其他说明

    数据范围:n<=100。

     

    代码:

     1 #include<iostream>
     2 
     3 using namespace std;
     4 
     5 int b[110],c[110],a[110];
     6 
     7 //    分别定义了三个数组:数组a(用来储存输入数据),数组b(用来存储从a[1]到a[n]的最大严格上升子序列长度),数组c(用来存储从a[n]到a[1]的最大严格下降子序列长度)
     8 
     9 int main()
    10 
    11 {
    12 
    13    
    14 
    15     int n,ans=0,i,j;
    16 
    17     scanf("%d",&n);//输入
    18 
    19     for(i=1;i<=n;i++) scanf("%d",&a[i]);
    20 
    21     b[1]=1;
    22 
    23 for(i=2;i<=n;i++)
    24 
    25 //从a[2]开始求最大严格上升子序列长度
    26 
    27        {
    28 
    29         b[i]=1;
    30 
    31         for(j=1;j<i;j++)
    32 
    33             if(a[i]>a[j]) b[i]=max(b[i],b[j]+1);
    34 
    35             //如果a[i]>a[j],则可能从a[1]到a[i]的最大严格上升子序列长度又增加了1。
    36 
    37     }
    38 
    39 c[n]=1;
    40 
    41 //从a[n]开始求最大严格上升子序列长度
    42 
    43     for(i=n-1;i>=1;i--)
    44 
    45        {
    46 
    47         c[i]=1;
    48 
    49         for(j=n;j>i;j--)
    50 
    51             if(a[i]>a[j]) c[i]=max(c[i],c[j]+1);
    52 
    53             //如果a[i]<a[j],则可能从a[i]到a[n]的最大严格上升子序列长度又增加了1。
    54 
    55     }
    56 
    57     for(i=1;i<=n;i++)
    58 
    59         if(b[i]+c[i]>ans) ans=b[i]+c[i];//更新答案
    60 
    61     printf("%d",n-ans+1);//输出
    62 
    63 }
    View Code

    《合唱队形》这道题其实就是《求最长下降/上升子序列》的翻版。但如果用循环直接搜固然很难办,而且不知道会用多长时间,所以,简单动态规划的思想很容易办到。

    b[i]=1;

    这句赋值语句固然很好理解,每一个元素,也可以视为一个符合题意的子序列,不论是最长上升、最长下降。所以从a[1]

    a[i]的符合题意的子序列必将有一个。比如有如下一组数。

    b[1]  1,原因如上。

    b[2]呢?如图,它显然比a[1]高,在执行如下语句时

    for(j=1;j<i;j++)   if(a[i]>a[j])

    j小于i,也就是2,目前符合条件的只有a[1]a[1]又通过了判断语句,它确实小于a[i],执行下一条语句:

    b[i]=max(b[i],b[j]+1);

    b[2]显然原来是1,当它和b[1]+1比时,1当然比2小,所以,b[2]自然就是2了。由此,这个方法只需要一维数组就行了。、

    O(nlogn)的方法,将下次介绍。这样的方法的时间复杂度为O(n^2)


    b[1]=1;

    解决完了这个问题,这道题就非常简单了,先从下往上搜一遍最长上升子序列,代码如下:

    for(i=2;i<=n;i++)

           {

            b[i]=1;

            for(j=1;j<i;j++)

                if(a[i]>a[j]) b[i]=max(b[i],b[j]+1);

    }

    再反着从上往下搜一下最长上升子序列:

    这段代码一定就很好理解了:

    for(i=1;i<=n;i++)

            if(b[i]+c[i]>ans) ans=b[i]+c[i];

    一直更新ans,要求出队人数最少,自然要让保留人数最多。

    最后输出n-ans+1

  • 相关阅读:
    Linux 多进程锁的几种实现方案
    Linux man手册没有pthread_mutex_init的解决办法
    IP地址结构信息与字符串相互转化:inet_pton和inet_ntop, etc.
    Linux 将计算md5值功能做成md5命令
    Unix/Linux inet守护进程
    Unix/Linux syslogd守护进程 & 日志记录syslog
    UNP 学习笔记 #11 名字与地址转换
    git 使用总结
    AUPE 输出致标准错误的出错函数分析与实现 err_sys, err_quit, err_doit etc.
    Linux C常见数I/O函数比较: printf, sprintf, fprintf, write...
  • 原文地址:https://www.cnblogs.com/wxjor/p/5524420.html
Copyright © 2011-2022 走看看