打印
[其他ST产品]

ST表--倍增区间DP处理RMQ

[复制链接]
549|14
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
一.简单介绍
预处理:
①区间DP   转移方程  f[i][j] = max(f[i][j - 1],f[i + ][j - 1])  f[i][j]表示从i位置开始的后2^j个数中的最大值

这里分成两半,一半是   即    ~    和 ~

②不过区间在增加时,每次并不是增加一个长度,而是基于倍增思想,用二进制右移,每次增加2^i个长度 ,最多增加logn次

这样预处理了所有2的幂次的小区间的最值

(其实快速幂是倍增法很好的例子)

使用特权

评论回复
评论
个百zz分点个 2023-4-21 23:38 回复TA
———————————————— 版权声明:本文为CSDN博主「zjyang12345」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/zjyang12345/article/details/89363091 
沙发
个百zz分点个|  楼主 | 2023-4-21 23:38 | 只看该作者
查询:
③对于每个区间,分成两段长度为的区间,再取个最值

(这里的两个区间是可以有交集的,因为重复区间并不影响最值)

比如3,4,6,5,3一种分成3,4,6和6,5,3,另一种分成3,4,6和5,3,最大值都是6,没影响。



④所以O(nlogn)预处理,O(1)查询最值  但不支持修改

使用特权

评论回复
板凳
个百zz分点个|  楼主 | 2023-4-21 23:38 | 只看该作者
二.代码介绍
①预处理外层for上界:求到n最少需要加上2的k次方,即写成k=log2(n)

然后注意先换底公式(经qq提醒,或可直接用log2函数),然后转换成double,最后应取下界(即取int)防溢出

使用特权

评论回复
地板
个百zz分点个|  楼主 | 2023-4-21 23:38 | 只看该作者
②初始化f[i][0]=a[i]:   即  j=0显然区间[i,i]里只有它一个数

使用特权

评论回复
5
个百zz分点个|  楼主 | 2023-4-21 23:38 | 只看该作者
③预处理两层for:外层j内层i,把j放外面,因为上界由i和j共同决定,而且区间长度由j决定,保证dp从小区间合并为大区间

注意内层循环,区间右端点下标为i+2^j-1,-1别漏

使用特权

评论回复
6
个百zz分点个|  楼主 | 2023-4-21 23:39 | 只看该作者
④预处理   f[i][j] = max(f[i][j - 1],f[i + 2^(j - 1)][j - 1])  或者·min

使用特权

评论回复
7
个百zz分点个|  楼主 | 2023-4-21 23:39 | 只看该作者
⑤查询划分点:

左区间右端点2^p:求l到r最少需要加上2的p次方,即写成p=log2(r-l+1),后面同①

右区间左端点k  :证明:  [k][k+-1]<=>[k][r]     k=r-2^p+1   因为p与区间长度有关,有时用len数组预处理某区间长度的k值

注意查询的l和r之间区间长度不一定刚好是2的次方,比如下图l,r,让区间重叠是没问题的

使用特权

评论回复
8
个百zz分点个|  楼主 | 2023-4-21 23:39 | 只看该作者
⑥取两个区间最值的最值

使用特权

评论回复
9
个百zz分点个|  楼主 | 2023-4-21 23:40 | 只看该作者
三.模板  P3865
①P3865  部分参考ttoj

#include <bits/stdc++.h>
using namespace std;
#define N 500010   
int maxl[N][16], minl[N][16];
int  a[N];
int n,m;
void S_table(){
    int l = int(log((double)n)/log(2.0));
    for (int j=1;j<=l;j++){
        for (int i=1; i + (1 << (j-1) ) - 1 <=n;++i){
            maxl[i][j] = max(maxl[i][j-1], maxl[i + (1 << (j-1) )][j-1]);
            minl[i][j] = min(minl[i][j-1], minl[i + (1 << (j-1) )][j-1]);
        }
    }
}
int rmq(int l, int r){
    int k = int(log((double)(r-l+1))/log(2.0));
    int a1 = max(maxl[l][k], maxl[r - (1<<k) + 1][k]);
    int a2 = min(minl[l][k], minl[r - (1<<k) + 1][k]);
    return a1;
    //printf("Max: %d Min: %d\n", a1, a2);
}


int main()
{
        int x,y;
        cin>>n>>m;
   for (int i=1;i<=n;++i)
    {
    scanf("%d", &a[i]);
    maxl[i][0] = minl[i][0] = a[i];
    }
    S_table();
    while(m--)
    {
            scanf("%d %d",&x,&y);
            printf("%d\n",rmq(x, y));
        }
   
}

使用特权

评论回复
10
个百zz分点个|  楼主 | 2023-4-21 23:40 | 只看该作者
②poj 3264 裸题,最大-最小
#include <iostream>
#include<algorithm>
#include<stdio.h>
#include<cmath>
using namespace std;
#define N 500010   
int maxl[N][16], minl[N][16];
int  a[N];
int n,m;
void S_table(){
    int l = int(log((double)n)/log(2.0));
    for (int j=1;j<=l;j++){
        for (int i=1; i + (1 << (j-1) ) - 1 <=n;++i){
            maxl[i][j] = max(maxl[i][j-1], maxl[i + (1 << (j-1) )][j-1]);
            minl[i][j] = min(minl[i][j-1], minl[i + (1 << (j-1) )][j-1]);
        }
    }
}
int rmq(int l, int r){
    int k = int(log((double)(r-l+1))/log(2.0));
    int a1 = max(maxl[l][k], maxl[r - (1<<k) + 1][k]);
    int a2 = min(minl[l][k], minl[r - (1<<k) + 1][k]);
    return a1-a2;
    //printf("Max: %d Min: %d\n", a1, a2);
}


int main()
{
        int x,y;
        cin>>n>>m;
   for (int i=1;i<=n;++i)
    {
    scanf("%d", &a[i]);
    maxl[i][0] = minl[i][0] = a[i];
    }
    S_table();
    while(m--)
    {
            scanf("%d %d",&x,&y);
            printf("%d\n",rmq(x, y));
        }
   
}

使用特权

评论回复
11
个百zz分点个|  楼主 | 2023-4-21 23:40 | 只看该作者
③华工校赛

区间gcd题解代码(gcd类似最值的性质)
#include<bits/stdc++.h>

using namespace std;

#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=a; i<=b; ++i)
#define PER(i,a,b) for(int i=a; i>=b; --i)
#define MP make_pair
#define PB push_back
#define fi first
#define se second
typedef long long LL;
typedef double DB;

const int maxn = 5e5;
const int Step = 17;
const int P = 1e9+7;

LL Gcd(LL a, LL b) { return b ? Gcd(b,a%b) : a; }

int n, ai[maxn+5];
int st[maxn+5][Step+2];

int len[maxn+5];

int Que(int l, int r) {
    int k = len[r-l+1];
    return Gcd( st[l][k], st[r-(1<<k)+1][k] );
}

int main() {
    scanf("%d", &n);
    REP(i,1,n) scanf("%d", ai+i), st[i][0] = ai[i];
    for(int k = 1; (1<<k) <= n; ++k) {
        for(int i = 1; i+(1<<k)-1 <= n; ++i) {
            st[i][k] = Gcd(st[i][k-1], st[i+(1<<(k-1))][k-1]);
        }
    }
    for(int i = 1; i <= n; ++i) {
        len[i] = len[i-1];
        if((1<<(len[i]+1)) < i) ++len[i];
    }
    LL ans = 0;
    REP(i,1,n) {
        int now = 0;
        for(int k = i, nx; k <= n; k = nx+1) {
            now = Gcd(now, ai[k]);
            int l = k+1, r = n;
            nx = k;
            while(l<=r) {
                int mid = (l+r)>>1;
                int tmp = Que(k,mid);
                if(tmp%now==0) nx = mid, l = mid+1;
                else r = mid-1;
            }
            ans = (ans + LL(nx-k+1) * now) % P;
        }
    }
    printf("%lld\n", ans);
    return 0;
}

使用特权

评论回复
12
童雨竹| | 2024-6-6 07:02 | 只看该作者

根据色彩的变化记录每一行的颜色跳变点,由此识别出车牌区域。

使用特权

评论回复
13
Wordsworth| | 2024-6-6 08:05 | 只看该作者

切割完了第四个字符之后,再依次扫描剩下的空间,直到所扫描的这一竖上的所有点的灰度值不全为0时,认为是字符的开始并依次扫描直到所扫描的这一竖上的所有点的灰度值全为0时认为是字符的结束。

使用特权

评论回复
14
Clyde011| | 2024-6-6 09:08 | 只看该作者

需要设定一个阈值来对像素点进行设置

使用特权

评论回复
15
公羊子丹| | 2024-6-6 10:01 | 只看该作者

计算量小,计算快。缺点也严重:在不同的图像中,颜色分布差别大,处理效果也不会很好。

使用特权

评论回复
16
万图| | 2024-6-6 11:04 | 只看该作者

在内存中开辟七个长为车牌长的七分之一和宽为车牌宽的区域

使用特权

评论回复
17
Uriah| | 2024-6-6 12:07 | 只看该作者

二值化就是让图像的像素点矩阵中的每个像素点的灰度值为0(黑色)或者255(白色

使用特权

评论回复
18
帛灿灿| | 2024-6-6 14:03 | 只看该作者

分别记录车牌区域的上下高度。然后通过RGB-HSV颜色转换

使用特权

评论回复
19
Bblythe| | 2024-6-6 15:06 | 只看该作者

通过OV7670摄像头进行图像采集

使用特权

评论回复
20
周半梅| | 2024-6-6 17:02 | 只看该作者

图像由前景和背景组成,在灰度直方图上,前景和背景会形成高峰,在双峰之间的最低谷处就是阈值。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

50

主题

631

帖子

0

粉丝