zoukankan      html  css  js  c++  java
  • P1816 忠诚

    P1816 忠诚

    题目描述

    老管家是一个聪明能干的人。他为财主工作了整整10年,财主为了让自已账目更加清楚。要求管家每天记k次账,由于管家聪明能干,因而管家总是让财主十分满意。但是由于一些人的挑拨,财主还是对管家产生了怀疑。于是他决定用一种特别的方法来判断管家的忠诚,他把每次的账目按1,2,3…编号,然后不定时的问管家问题,问题是这样的:在a到b号账中最少的一笔是多少?为了让管家没时间作假他总是一次问多个问题。

    输入格式

    输入中第一行有两个数m,n表示有m(m<=100000)笔账,n表示有n个问题,n<=100000。

    第二行为m个数,分别是账目的钱数

    后面n行分别是n个问题,每行有2个数字说明开始结束的账目编号。

    输出格式

    输出文件中为每个问题的答案。具体查看样例。

    输入输出样例

    输入 #1
    10 3
    1 2 3 4 5 6 7 8 9 10
    2 7
    3 9
    1 10
    输出 #1
    2 3 1

    当时看着标签是dp才做的,结果发现是一个数据结构体qwq

    那就来复习一下数据结构吧

    区间最值,很容易想到一下几种思路:

    线段树,树状数组,st表

    我自己的方法是线段树,AC代码如下(很简单的板子)

    #include<bits/stdc++.h>
    using namespace std;
    
    int a[502000],t[502000];
    int n,m;
    
    void build(int p,int l,int r)
    {
        if(l==r) 
        {
            t[p]=a[l];
            return;
        }
        
        int mid=(l+r)/2;
        build(2*p,l,mid);
        build(2*p+1,mid+1,r);
        t[p]=min(t[2*p],t[2*p+1]);
    }
    
    int find(int x,int y,int l,int r,int p)
    {
        if(x<=l&&y>=r) 
        {
            return t[p];
        }
        
        int mid=(l+r)/2;
        int a=9999999,b=9999999;
        if(x<=mid) a=find(x,y,l,mid,2*p);
        if(y>mid) b=find(x,y,mid+1,r,2*p+1);
        return min(a,b);
    }
    
    int main()
    {
        cin>>n>>m;
        for(int i=1;i<=n;i++) cin>>a[i];
        
        build(1,1,n);
        
        while(m--)
        {
            int x,y;
            cin>>x>>y;
            cout<<find(x,y,1,n,1)<<" ";
        }
        
        return 0;
        
    }

    题解中的其他方法:

    树状数组:

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    int a[1000001];    
    int tree[1000001];   
    int n,m;
    int lowbit(int x)
    {
        return x&(-x);
    }
    void update(int x,int v)
    {
        while(x<=n)
        {
            if(tree[x]>v)
              tree[x]=v;
             else
              return;     
            x+=lowbit(x);  
        }
    }
    int getmin(int x,int y)
    {
        int now=y;
        int maxl=2147483647;
        while(now>=x)
        {
            if(now-lowbit(now)>x)
            {
               maxl=min(maxl,tree[now]);
               now-=lowbit(now);
            }
            else
             {
                 maxl=min(maxl,a[now]);
                 --now;
             }
        }
        return maxl;
    }
    int main()
    {
        memset(tree,127,sizeof(tree));
        cin>>n>>m;
        int x,y;
        //n++;
        for(int i=1;i<=n;++i)
         {
             cin>>a[i];
             update(i,a[i]);
         }
        for(;m>0;--m)
        {
            cin>>x>>y;
            cout<<getmin(x,y)<<' ';
        }
        return 0; 
    }

    st表:

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    int a[100005][20],m,n;
    inline int getint()                    //读入优化
    {
        int a=0;char x=getchar();bool f=0;
        while((x<'0'||x>'9')&&x!='-')x=getchar();
        if(x=='-')f=1,x=getchar();
        while(x>='0'&&x<='9')
        {a=a*10+x-'0';x=getchar();}
        return f?-a:a;
    }
    void rmq_st(int n)                //预处理,st表
    {
        for(int j=1;j<=20;j++)
        for(int i=1;i<=n;i++)           //思考为什么外循环j套i,而不是外循环i套j
        if(i+(1<<j)-1<=n)
        a[i][j]=min(a[i][j-1],a[i+(1<<(j-1))][j-1]);   //原因在于每个区间更新都是由1个点开始慢慢增加区间范围
    }
    int main()
    {
        m=getint();n=getint();
        for(int i=1;i<=m;i++)a[i][0]=getint();   //读入数据,对于每个点开始时最小值就是它本身
        rmq_st(m);
        while(n--)
        {
            int l=getint(),r=getint(),k=log2(r-l+1);
            printf("%d ",min(a[l][k],a[r-(1<<k)+1][k]));        //查询时注意思路 
        }
        return 0;
    }

    还有一种不知道是什么神奇方法的方法(看着像是一种贪心?)

    #include<bits/stdc++.h>
    using namespace std;
    struct fff{
      int x,y;  
    };
    fff a[100001];
    bool cmp(fff l,fff v){
        return l.x<v.x;
    }
    int main(){
        int m,n,t,k;
        scanf("%d%d",&m,&n);
            for(int i=1;i<=m;i++){
                scanf("%d",&a[i].x);
                a[i].y=i;    //为每个账目编号;
            }
            sort(a+1,a+1+m,cmp);
            for(int i=1;i<=n;i++){
                scanf("%d%d",&t,&k);
                for(int j=1;j<=m;j++){
                    if(a[j].y>=t&&a[j].y<=k){
                        printf("%d ",a[j].x);
                        break;     //一个小优化;
                    }
                }
            }
        return 0;
    }
    //主要是快排,先把账目排一遍,再找提问范围里靠前的,找到后就输出;(比较好理解)
  • 相关阅读:
    Delphi操作Excel大全
    一名Delphi程序员的开发习惯
    七维互联(www.7wei.com)
    Android开发数据库三层应用-DataSnap
    如何破解excel宏的密码
    让Delphi的DataSnap发挥最大效率
    使用 TRegistry 类[1]: 显示各主键下的项
    ini 文件操作记要(1): 使用 TIniFile
    Delphi经验总结(1)
    Delphi经验总结(2)
  • 原文地址:https://www.cnblogs.com/yxr001002/p/14116107.html
Copyright © 2011-2022 走看看