nyoj ACM:士兵杀敌(三)(区间最值查询 --RMQ算法)

15 篇文章 0 订阅
11 篇文章 0 订阅

士兵杀敌(五)
时间限制:2000 ms | 内存限制:65535 KB
难度:5
描述
南将军麾下有百万精兵,现已知共有M个士兵,编号为0~M,每次有任务的时候,总会有一批编号连在一起人请战(编号相近的人经常在一块,相互之间比较熟悉),最终他们获得的军功,也将会平分到每个人身上,这样,有时候,计算他们中的哪一个人到底有多少军功就是一个比较困难的事情。

在这样的情况下,南将军却经常会在许多次战役之后询问军师小工第i号士兵到第j号士兵所有人的总军功数。

请你帮助军师小工回答南将军的提问。

输入
只有一组测试数据
第一行是三个整数N,C,Q(1<=N,C,Q<=1000000),其中N表示士兵的总数。
随后的C行,每行有三个整数Mi,Ni,Ai(0<=Mi<=Ni<=N,0<=Ai<=100),表示从第Mi号到第Ni号士兵所有人平均增加了Ai的军功。
再之后的Q行,每行有两个正正数m,n,表示南将军询问的是第m号士兵到第n号士兵。
输出
请对每次询问输出m号士兵到第n号士兵的总军功数,由于该数值可能太大,请把结果对10003取余后输出
样例输入
5 3 2
1 3 2
2 4 1
5 5 10
1 5
2 3
样例输出
19
6

对于区间最值查询问题,最容易想到的解决方案是遍历,复杂度是O(n)。但当数据量非常大且查询很频繁时,该算法也许会存在问题。
一种比较高效的在线算法(ST算法)解决这个问题。ST(Sparse Table)算法是一个非常有名的在线处理RMQ问题的算法,它可以在O(nlogn)时间内进行预处理,然后在O(1)时间内回答每个查询。具体见博客http://blog.csdn.net/mosbest/article/details/68937109

我的代码:
1. 这一次与士兵杀敌(二)一样,在运算符优先级那里出了问题,卡在那,找半天才找到了原因。
2. 要想在定义全局数组A[NN][NN],NN必须是常量,可以用 const int NN=5; 或者 #define NN 5 但是//const int NN =(int)(log(100100)/log(2)); 和//#define NN (int)(log(100100)/log(2)) 是不行的。
3. 从最优代码中发现,他是用左移<< 和 右移 >> 来表示2^n的。而我用的是math库函数,pow(2,n)。很显然,左移的速度远远大于库函数 。 速度 左移 > 2*2*2….循环n次 > pow(2,n)

#include<iostream> 
#include<cstdio>
#include<cmath>
//有半天卡在了优先级上面了 
using namespace std;
int a[100100];
int MAX[100100][ 22+1];
int MIN[100010][22 +1];
int N,Q;
int main()
{
    scanf("%d%d",&N,&Q);

    for(int i=1;i<=N;i++)
        scanf("%d",a+i);

    //st算法 动态规划 初始化 max min
    for(int i=1;i<=N;i++)
    {
        MAX[i][0]=a[i];
        MIN[i][0]=a[i];
    }

    for( int j=1;j<=(log(N)/log(2));j++)
    {
        for(int i=1;i<=N-pow(2,j)+1;i++)
        {
            MAX[i][j]=max(MAX[i][j-1],MAX[i+(int)pow(2,j-1)][j-1]);
            MIN[i][j]=min(MIN[i][j-1],MIN[i+(int)pow(2,j-1)][j-1]);
        }
    }

    int m,n,k,e,f;
    for(int i=0;i<Q;i++)
    {
        scanf("%d%d",&m,&n);
        k=(int)(log(n-m+1)/log(2));
        e=max(MAX[m][k],MAX[n-(int)pow(2,k)+1][k]);
        f=min(MIN[m][k],MIN[n-(int)pow(2,k)+1][k]);
        printf("%d\n",e-f);
    }

    return 0;
}

最优代码:


#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const int MAX=100010;
int FMAX[MAX][20],FMIN[MAX][20];

int main()
{
    int n,q,a,b,v;
    cin>>n>>q;
    for(int i=1;i<=n;++i)
    {
        scanf("%d",&FMAX[i][0]);
        FMIN[i][0]=FMAX[i][0];
    }
    for(int i=1;i!=20;i++)
        for(int j=1;j<=n;j++)
            if(j+(1<<i)-1<=n)
            {
                FMAX[j][i]=max(FMAX[j][i-1],FMAX[j+(1<<(i-1))][i-1]);
                FMIN[j][i]=min(FMIN[j][i-1],FMIN[j+(1<<(i-1))][i-1]);
            }
    for(int i=0;i!=q;++i)
    {
        scanf("%d%d",&a,&b);
        int len=(int)(log(b-a+1.0)/log(2.0));
        printf("%d\n",max(FMAX[a][len],FMAX[b-(1<<len)+1][len])-min(FMIN[a][len],FMIN[b-(1<<len)+1][len]));
    }
}        
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值