• 【算法】ST表


    想学习一下LCA倍增,先 水一个黄题 学一下ST表

    ST表

    介绍:

    这是一个运用倍增思想,通过动态规划来计算区间最值的算法

    算法步骤:

    1. 求出区间最值

    2. 回答询问

    求出区间最值:

    f[i][j]来存储从第 j 个点开始,向后 2 ^ i - 1 个点中的最值(包括本身)

    利用二分法的思想,将区间 [ j,j +(2 ^ i)- 1 ] 平均(大概)分成两半

    可以算出,区间 [ j,j +(2 ^ i)- 1 ] 的长度为 2 ^ i

    所以一半的长度为 2 ^ i - 1

    那么分成的两个区间就为 [ j,j +(2 ^(i - 1)- 1 ] 和 [ j +(2 ^ i - 1 ),j +(2 ^ i)- 1 ] 。

    毫无疑问,
    f[i][j] = max(f[i - 1][j],f[i - 1][j +(1 << i - 1)])

    这样递推式就出来了

    现在来想一下:
    f[0][j]就是从 j 开始向后数第 2 ^ 0 - 1 个点的最值,区间为 [ j,j ]

    不用考虑,f[0][j]就是第 j 个数本身

    好了,现在边界也得出来了,可以开始 dp 了

    上代码:

    void prew() {
    	F1(i, 1, n) f[0][i] = a[i]; // 给边界赋值,a[i] 存的是数列的第 i 个数
    	int kf = log2(n); // 得出数列最多可以向后跳几个 2 的幂,n 为数列长度
    	F1(i, 1, kf) { // 枚举区间的长度 2 ^ i
    	      for (int j = 1; j + (1 << i) - 1 <= n; j++) // 枚举起点
    		      f[i][j] = max(f[i - 1][j], f[i - 1][j + (1 << i - 1)]); // 递推式
    	}
    }
    

    回答询问:

    由于 log2 运算可能会出现实数,而我们又用整数类型来存储,所以可能会出现两个区间不能完全覆盖整个区间的情况,得出的 f[i][j]不够精准

    最简单的方法就是用两个区间覆盖

    反正又没要求两个区间不能重叠~~

    可以选用f[k][l]f[k][r-(1<<k)+1]来覆盖f[l][r]

    所以f[l][r] = max(f[k][l],f[k][r -(1 << k)+ 1])(k 为区间 [l,r] 的长度的 log2)

    再上代码:

    int ask(int l, int r) {
    	int k = log2(r - l + 1); // 求出区间最大的 log2 值
    	return max(f[k][l], f[k][r - (1 << k) + 1]); // 返回区间 [l,r] 的最大值
    }
    

    完整代码:

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <iomanip>
    #include <cmath>
    #include <algorithm> // 妈妈再也不怕我的头文件不够使啦~~
    #define MAXN 100100
    #define INF 0x3f3f3f3f
    #define LL long long
    #define F1(i, a, b) for (LL i = a; i <= b; ++i) // 懒人必备神器
    #define F2(i, a, b) for (LL i = a; i >= b; --i)
    using namespace std;
    
    int f[31][MAXN], a[MAXN];
    //f[i][j]表示从 j 往后 2 ^ i - 1 个数中的最大值 
    int n, m;
    
    inline int read() { // 快读
    	int sto = 0, fg = 1;
    	char ch = getchar();
    	while (ch < '0' || ch > '9') {
    		if (ch == '-') fg = -1;
    		ch = getchar();
    	}
    	while (ch >= '0' && ch <= '9') {
    		sto = (sto << 1) + (sto << 3) + (ch ^ 48);
    		ch = getchar();
    	}
    	return sto * fg;
    }
    
    void prew() { // 预处理 dp
    	F1(i, 1, n) f[0][i] = a[i];
    	int kf = log2(n);
    	F1(i, 1, kf) {
    		for (int j = 1; j + (1 << i) - 1 <= n; j++)
    			f[i][j] = max(f[i - 1][j], f[i - 1][j + (1 << i - 1)]);
    	}
    }
    
    int ask(int l, int r) { // 回答询问
    	int k = log2(r - l + 1);
    	return max(f[k][l], f[k][r - (1 << k) + 1]);
    }
    
    int main()
    {
    	int l, r, ans;
        n = read(); m = read();
        F1(i, 1, n) a[i] = read();
        prew();
        F1(i, 1, m) {
        	l = read(); r = read();
        	ans = ask(l, r);
        	printf("%d
    ", ans);
        }
        return 0;
    }
    

    模板题:
    洛谷P3865

  • 相关阅读:
    Java多线程(二)关于多线程的CPU密集型和IO密集型这件事
    Java 过一下基础
    日历打印用java实现
    DAY8-打卡第八天-2018-1-18
    web-day1-初识标识符
    DAY7-图形界面第一弹-2018-1-17
    四种排序方法用java实现
    DAY6-小变化(java提示框)-2018-1-16
    DAY5-小别-2018-1-15
    DAY4-打卡第四天-2018-1-12
  • 原文地址:https://www.cnblogs.com/EiffelA-blog/p/13798331.html
Copyright © 2020-2023  润新知