zoukankan      html  css  js  c++  java
  • 最高分是多少

    最高分是多少

    题目描述

    老师想知道从某某同学当中,分数最高的是多少,现在请你编程模拟老师的询问。当然,老师有时候需要更新某位同学的成绩.

    输入描述:

    输入包括多组测试数据。
    每组输入第一行是两个正整数N和M(0 < N <= 30000,0 < M < 5000),分别代表学生的数目和操作的数目。
    学生ID编号从1编到N。
    第二行包含N个整数,代表这N个学生的初始成绩,其中第i个数代表ID为i的学生的成绩
    接下来又M行,每一行有一个字符C(只取‘Q’或‘U’),和两个正整数A,B,当C为'Q'的时候, 表示这是一条询问操作,他询问ID从A到B(包括A,B)的学生当中,成绩最高的是多少
    当C为‘U’的时候,表示这是一条更新操作,要求把ID为A的学生的成绩更改为B。

    输出描述:

    对于每一次询问操作,在一行里面输出最高成绩.
    示例1

    输入

    5 7
    1 2 3 4 5
    Q 1 5
    U 3 6
    Q 3 4
    Q 4 5
    U 4 5
    U 2 9
    Q 1 5
    

    输出

    5
    6
    5
    9

    分析:

    这个题目树状数组线段树都是OK的,但是没有涉及到对区间的操作,树状数组在时间和空间上面的代价都要小很多。

    牛客网题解:

    暴力,线段树,块状链表

    链接:https://www.nowcoder.com/questionTerminal/3897c2bcc87943ed98d8e0b9e18c4666
    来源:牛客网

    华为仿佛找了一种比较奇特的方法来区分应聘者啊。

    先来说说这题的3种做法:
    最简单的就是暴力了。每次查询直接做。
    修改复杂度O(1),查询复杂度O(N)
     
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    #include <stdio.h>
    #include <algorithm>
    using namespace std;
     
    const int MAXN=100000;
     
    int data[MAXN+5];
     
    int querymax ( int l , int r ) {
        int ans=data[l];
        for(int i=l+1;i<=r;i++) ans=max(ans,data[i]);
        return ans;
    }
     
    void update(int idx,int value){
        data[idx]=value;
    }
     
    int main(){
        int n,m;
        while(~scanf("%d%d",&n,&m)){
            for(int i=1;i<=n;i++){
                scanf("%d",&data[i]);
            }
            char order;
            int a,b;
            for(;m--;){
                scanf(" %c%d%d",&order,&a,&b);
                if(order=='U'){
                    update(a,b);
                }else if(order=='Q'){
                    if(a>b)swap(a,b);
                    printf("%d ",querymax(a,b));
                }
            }
        }
        return 0;
    }
    ……如果他们手一抖,写成N<=100000,M<=100000怎么办? 
    !!!注意:以下内容涉及高级数据结构!!!
    首先,有ACM经验的选手,一看这个题,第一反应,线段树好~
    线段树这种数据结构,修改O(logn),查询O(logn),但是要预处理O(nlogn)
    这个,如果没有ACM经验,也不打算认真做ACM的同学,看看就好。
     
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    #include <stdio.h>
    #include <algorithm>
    using namespace std;
     
    const int MAXN=100000;
     
    int data[MAXN+5];
    int maxarr[MAXN*4+5];
     
    void build(int p,int l,int r) {
        if(l==r){
            maxarr[p]=data[l];
            return ;
        }
        int m = ( l + r ) >> 1 ;
        int lchild=p<<1,rchild=p<<1|1;
        build ( lchild , l , m ) ;
        build ( rchild , m+1 , r ) ;
        maxarr[p]=max(maxarr[lchild],maxarr[rchild]);
    }
     
    int querymax ( int L , int R , int p , int l , int r ) {
        if ( L <= l && r <= R ) {
            return maxarr[p];
        }
        int m = ( l + r ) >> 1 ;
        int lans=-1,rans=-1;
        if ( L <= m ) lans=querymax ( L , R , p << 1 , l , m ) ;
        if ( m <  R ) rans=querymax ( L , R , p << 1 | 1 , m + 1 , r ) ;
        if(lans==-1)return rans;
        if(rans==-1)return lans;
        return max(lans,rans);
    }
     
    void update(int idx,int value,int p,int l,int r){
        if(l==r&&l==idx){
            maxarr[p]=value;
            return;
        }
        int m = ( l + r ) >> 1 ;
        if ( idx <= m ) update( idx, value, p << 1, l, m );
        if ( m <  idx ) update( idx, value, p << 1|1, m+1, r );
        maxarr[p]=max(maxarr[p<<1],maxarr[p<<1|1]);
    }
     
    int main(){
        int n,m;
        while(~scanf("%d%d",&n,&m)){
            for(int i=1;i<=n;i++){
                scanf("%d",&data[i]);
            }
            build(1,1,n);
            char order;
            int a,b;
            for(;m--;){
                scanf(" %c%d%d",&order,&a,&b);
                if(order=='U'){
                    update(a,b,1,1,n);
                }else if(order=='Q'){
                    if(a>b)swap(a,b);
                    printf("%d ",querymax(a,b,1,1,n));
                }
            }
        }
        return 0;
    }
     有没有什么办法,普通人能去想出来,又不需要专门去学过高级数据结构呢?
    有一个,叫块状链表(其实块状数组也行吧)。
    好吧,其实分块的思想也不怎么常见,谈针对面试的算法的思路,都谈分治、贪心、递推、动态规划,没见人说过分块的样子。但是,分块是比较容易理解的。
    很简单,现在我们把整个N大小的数组按顺序拆成sqrt(n)(根号n)个小数组,每个小数组有sqrt(n)个元素
    比如 1 2 3 4 5 6 7 8 9
    现在拆成
    1 2 3
    4 5 6
    7 8 9
    然后对每一个小块,我们除了改掉相应位置的值,还要额外记录一下整个小块的最大值。
    如果我更新的时候,那个小块的最大值增大,那很简单,最大值也增大了。
    如果把最大值改小了呢?为了正确性,只能把整个小块扫一遍,重新算出最大值了。
    所以,修改的复杂度是O(sqrt(n))
    现在看查询。我们要充分利用分小块以后的信息。
    比如要查询2到9的最大值。按之前最朴素的暴力的做法,我要访问2、3、4、5、6、7、8、9
    现在有小块的最大值信息了,我只要判断每个小块是否在查询区间内,不在的没用,一部分在的,就暴力查找,如果是完整在查询区间内的,我们就利用之前算好的这个小块内的最大值。
    所以,分块的情况下,查询2到9的最大值,需要看看2、3,以及4~6的最大值,7~9的最大值。
    很容易证明,查询的复杂度是O(sqrt(n))的(最坏是sqrt(n)个块全部要用,左右2边只盖住sqrt(n)-1个数,要暴力遍历过去)
    //TODO:在这里补上分块法的代码

    3种流派全部可过,但是明显的,在极限数据情况下,能够轻易区分出普通应聘者,会动脑的应聘者和有ACM经验的应聘者了。
  • 相关阅读:
    链表操作
    建立简单的链表
    scanf()函数用法小结
    快速求某年某月是第几天(未整理)
    合并两个有序数组(未整理)
    如何配置JAVA的环境变量、Tomcat环境变量
    【动态规划】流水作业调度问题与Johnson法则
    分治法(二)
    分治法(一)
    HTML5入门十一---Canvas画布实现画图(二)
  • 原文地址:https://www.cnblogs.com/Renyi-Fan/p/7779097.html
Copyright © 2011-2022 走看看