51Nod_1174区间中最大值

题目要求,先输入一个数组,然后求出数组中某一个区间中的最大值。

是不是觉得,我就直接用for循环就可以写啦,贼好写。。

这样子讲吧,如果你只搜索一次的话,是看不出什么区别的,但是如果你要搜索M次,M又很大呢? 每一次都要用循环去一个一个比较吗? O(n*M)的时间复杂度是不是会让你体验极差、、

这个题目在经过自己思考与借鉴其他人的博客以后,判断应该是有两种方法可以解决这个问题,分别是线段树和RMQ算法。

先使用线段树算法来做这个题吧,线段树是一个近似于完全二叉树的结构,具体对线段树的了解可以参考这个网站:

https://blog.csdn.net/x314542916/article/details/7837276。。。感觉还可以,话不多说上代码:

#include <iostream>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
using namespace std;
const int MAX = 10003;  
int maxv[MAX*3];
int data[MAX];
int n;
void build(int node,int begin,int end){
	if(begin == end){
		maxv[node] = data[begin];
	}else{
		int m = (begin+end)/2;
		build(2*node,begin,m);
		build(2*node+1,m+1,end);
		if(maxv[2*node] >= maxv[2*node+1])
			maxv[node] = maxv[2*node];
		else
			maxv[node] = maxv[2*node+1];
	}
}

int query(int k,int l,int r,int ql,int qr){
	if(ql <= l && r <= qr)
		return maxv[k];
	int m = l+(r - l)/2;
	int ans = -1;
	if(ql <= m) ans = max(ans,query(k*2,l,m,ql,qr));
	if(qr > m) ans = max(ans,query(2*k+1,m+1,r,ql,qr));
	return ans;
}
void update(int k,int l,int r,int p,int v){
	int m = l+(r-l)/2;
	if(l == r)
		maxv[k] = v;
	else{
		if(p <= m)
			update(k*2,l,m,p,v);
		else
			update(k*2+1,m+1,r,p,v);
		maxv[k] = max(maxv[k*2],maxv[k*2+1]);
	}
	
}
void print()  
{  
    for(int i=1;i<2*n;i++)  
       printf("%d ",maxv[i]);  
    printf("\n");  
 } 

int main()
{
	cin>>n;
	for(int i = 0;i < n;i ++){
		cin>>data[i];
	}
		build(1,0,n-1);
		int T;
		cin>>T;
		while(T --){
			int ql,qr;
			cin>>ql>>qr;
			cout<<query(1,0,n-1,ql,qr)<<endl;
		}	
	return 0;
}

还有一种做法叫RMQ算法,这个算法的灵性在于:用一个二维数组存储数据,在查询结果的时候,直接到二维数组中找结果就行了,取结果的时间复杂度为O(1);同时计算的过程的时间复杂度也只需O(n*logn),是一个很高效的算法。下文对RMQ算法的理解引用于https://blog.csdn.net/ACMore_Xiong/article/details/51920019;

在很多情况下, 我们求区间最大最小值都是用朴素的遍历算法,其复杂度是O(N), 当存在多次区间最大最小查询时,若查询次数为Q, 那么算法负责度就是O(Q*N) 。 当查询次数 Q 很大时,我们就需要对算法进行优化了。常见的优化方法有: 使用树状数组或者线段树,或者是使用专门的RMQ算法。RMQ是一种专门用来求区间最大最小值的DP。 
树状数组和线段树求区间最值,需要 O(N*log(N)) 的时间负杂度进行初始化,然后O(log(N))的时间负杂度进行查询。
RMQ算法求区间最值,需要O(N*log(N))的时间负杂度进行初始化,但是查询只需要O(1) 。
下面是我对RMQ算法的理解:
以求区间最大值为例,MAX[i][j] 表示的是从i 开始 长度为 2^j 的区间的最大值。

  1. 初始化的过程
    MAX[i][j] 表示的是区间 [i, i + (1 << j) - 1] 的最大值,区间长度是1<<j:
    将区间 [i, i + (1 << j) - 1] 分成两个长度都为 1 << (j - 1) 的区间 m = i + (1 << (j - 1)) - 1;
    [i, m]、[m + 1, m +  (1 << (j - 1)) - 1],
    那么, 我们写出状态转移方程
    MAX[i][j]  = max(MAX[i][j - 1], MAX[m + 1][j - 1])
       = max(MAX[i][j - 1], MAX[ i + (1 << (j - 1))][j - 1])

  2. 查询过程

假如查询区间为[L, R]。区间查询长度为 R - L + 1,令k = (int)log2(R - L + 1), 即小于区间长度的最大的2的幂次方。k 满足大小关系2^k <= (R-L+1)<=2^(k+1),那么区间[L, R]的最大值就必在区间[L,L + (1 << k) - 1], 或者区间[R-(1<<k) + 1,R]中。

最后给大家看看RMQ的算法代码,大家一起探讨学习啊。

#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <algorithm>
using namespace std;

#define FIN             freopen("input.txt","r",stdin)
#define FOUT            freopen("output.txt","w",stdout)
#define fst             first
#define snd             second
#define lson            l, mid, rt << 1
#define rson            mid + 1, r, rt << 1 | 1

typedef __int64  LL;
//typedef long long LL;
typedef unsigned int uint;
typedef pair<int, int> PII;

const int INF = 0x3f3f3f3f;
const double eps = 1e-6;
const int MAXN = 10000 + 5;
const int MAXM = 50000 + 5;
const int MAXBIT = 16;

int N, Q;
int a, b;
struct RMQ {
    int MAX[MAXN][MAXBIT];
    int log2[MAXN];
    int n, k;
    void init(int _n) {
        n = _n;
        log2[0] = -1;
        for (int i = 1; i <= n; i ++) {
            scanf("%d", &MAX[i][0]);
            if (i) log2[i] = i & (i - 1) ? log2[i - 1] : log2[i - 1] + 1;
        }
        for (int j = 1; j <= log2[n]; j ++) {
            for (int i = 1; i + (1 << j) - 1 <= n; i ++) {
                MAX[i][j] = max(MAX[i][j - 1], MAX[i + (1 << (j - 1))][j - 1]);
            }
        }
    }
    int query(const int& L, const int& R) {
        k = log2[R - L + 1];
        return max(MAX[L][k], MAX[R - (1 << k) + 1][k]);
    }
} rmq;
int main() {
#ifndef ONLINE_JUDGE
    FIN;
#endif // ONLINE_JUDGE
    while (~scanf("%d", &N)) {
        rmq.init(N);
        scanf("%d", &Q);
        while (Q --) {
            scanf("%d %d", &a, &b);
            a ++, b ++;
            int res = rmq.query(a, b);
            printf("%d\n", res);
        }
    }
    return 0;
}
今天就写到这里吧,每一天都为了不辜负下一天而奋斗!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值