ACM新手DAY 22 RMQ

题解

A - Balanced Lineup

题目:给定一组数,求指定区间的最大值和最小值。

  • RMQ标准问题
  • ST算法预处理及查询
  • 对应代码
//简单应用模板,但是注意最小花查询初始化的时候也要新起一个函数,不然最小化求得不对,不晓得怎么回事。。。
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=50000+100;

int dmax[maxn][20], dmin[maxn][20];

void initmax(int n,int d[])//初始化最大值查询
{
    for(int i=1; i<=n; i++)
        dmax[i][0]=d[i];
    for(int j=1 ; (1<<j)<=n ; j++)
        for(int i=1; i+(1<<j)-1 <=n; i++)
            dmax[i][j]=max(dmax[i][j-1],dmax[i+(1<<(j-1))][j-1]);
}
int getmax(int L,int R)//查询最大值
{
    int k=0;
    while((1<<(k+1))<=R-L+1)k++;
    return max(dmax[L][k], dmax[R-(1<<k)+1][k]);
}
void initmin(int n,int d[])
{
    for(int i=1; i<=n; i++)
        dmin[i][0]=d[i];
    for(int j=1; (1<<j)<=n; j++)
        for(int i=1; i+(1<<j)-1<=n; i++)
            dmin[i][j]=min(dmin[i][j-1],dmin[i+(1<<(j-1))][j-1]);
}
int getmin(int L,int R)
{
    int k=0;
    while( (1<<(k+1)) <=R-L+1)k++;
    return min(dmin[L][k],dmin[R-(1<<k)+1][k]);
}
int main()
{
    int n, t;
    scanf("%d%d", &n, &t);
    int a[maxn];
    for(int i=1; i<=n; i++)
        scanf("%d", &a[i]);
    initmax(n, a); initmin(n, a);
    while(t--)
    {
        int A, B;
        scanf("%d%d", &A, &B);
        int mx = getmax(A, B);
        int mn = getmin(A, B);
        printf("%d\n", mx-mn);
    }
    return 0;
}

B - A Magic Lamp

题目:读入一个整数,不改变顺序,删掉数位上m个数,留下的数字形成一个新的数,求这个数最小是多少

  • RMQ+思维
  • 思路:
  1. 分析:如178543,删掉4个数,留下的二位数的第一个数字一定在原数的前5位(4+1)里面,也就是在17854里面找第一个数,之后,从找到的1的下一位7开始,在78543这个区间里找。
  2. 做法:ST算法循环查找。
  • 获得一个char类型数组的长度:int len = strlen(ch)
  • 对应代码:
#include<stdio.h>
#include<iostream>
#include<math.h>
#include<string.h>
using namespace std;
const int MAXN=1010;
int a[MAXN];//a是把字符转成数字的
int dp[MAXN][20];
char str[MAXN];
int ans[MAXN];
void initmin(int n,int b[])//形成最小值下标的RMQ
{
    for(int i=0; i<n; i++)
        dp[i][0]=i;
    for(int j=1; (1<<j)<=n; j++)
        for(int i=0; i+(1<<j)-1<n; i++)
            dp[i][j]=b[dp[i][j-1]] <= b[dp[i+(1<<(j-1))][j-1]] ? dp[i][j-1]:dp[i+(1<<(j-1))][j-1];
    //这里一定要加等号,就是相等的时候取下标小的
}
int rmqIndex(int L,int R,int b[])
{
    int k=0;
    while( (1<<(k+1)) <=R-L+1)k++;
    return b[dp[L][k]] <= b[dp[R-(1<<k)+1][k]] ? dp[L][k]:dp[R-(1<<k)+1][k];
    //加等号,取小标小的
}
int main()
{
    int m;
    while(scanf("%s%d",&str,&m)!=EOF)
    {
        int n=strlen(str);
        for(int i=0; i<n; i++) a[i]=str[i]-'0';
        initmin(n,a);
        int pos=0, flag=0;
        for(int i=m; i<n; i++) //找n-m个数,每次从[t,i]中找最小的
        {
            pos=rmqIndex(pos,i,a);
            ans[flag++]=a[pos++];
        }
        //去前导零
        pos=0;
        while(pos<flag&&ans[pos]==0) pos++;
        if(pos>=flag)printf("0\n");
        else
        {
            for(int i=pos; i<flag; i++)printf("%d",ans[i]);
            printf("\n");
        }
    }
    return 0;
}

C - Maximum Absurdity

题目:在一组数里面里选出两个不交叉的长度为k的区间,使两个区间的值的和最大,输出两个区间第一个数的坐标。

  • 枚举吧。。感觉很经典
  • 设一个很大的数:#define INF 0x3f3f3f3f,设一个很小的数可以:-INF(前面的INF)
  • 代码部分
//枚举。。
#include<iostream>
#include<string.h>
#define INF 0x3f3f3f3f
#define ll long long
using namespace std;
const int maxn = 2e5 + 10;
ll a[maxn], ans[maxn];
int main()
{
    ll n, k;
    cin >> n >> k;

    memset(a, 0, sizeof a);
    memset(ans, 0, sizeof ans);

    for(int i=1; i<=n; i++)
    {
        cin >> a[i];
        a[i] = a[i] + a[i-1];//处理一下使每个储存的是它前面所以数字的和
    }
    for(int i=1; i<= n-(k-1); i++)
        ans[i] = a[i-1 + k] - a[i-1];

    ll L, R, pos;
    ll min1 = -INF, min2 = -INF;
    for(int i=n-(k-1); i-k>=1; i--)
    {
        if(min1 <= ans[i])
        {
            min1 = ans[i];
            pos = i;
        }
        if(min2 <= min1 + ans[i-k])
        {
            min2 = min1 + ans[i-k];
            L = i - k;
            R = pos;
        }
    }
    cout << L << " " << R << endl;
    return 0;
}

E - The Water Problem

题目:给一组数,求指定区间里面最大的数。

  • RMQ
  • ST算法,直接用A题的模板就行,只需要处理一下输入输出。

F-P1890 gcd区间

题目:给一组数,求出指定区间的最大公因数

  • RMQ变化
  • 把ST算法里的max换成gcd函数
  • gcd函数
int gcd(int a,int b)
{
    return b?gcd(b,a%b):a;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值