uva 11235 Frequent values(游程编码+区间最小值查询)

游程编码的基本原理是:用一个符号值或串长代替具有相同值的连续符号(连续符号构成了一段连续的“行程”。游程编码因此而得名),使符号长度少于原始数据的长度。只在各行或者各列数据的代码发生变化时,一次记录该代码及相同代码重复的个数,从而实现数据的压缩。


游程编码(Run Length Encoding , RLE)

例如:5555557777733322221111111
游程编码为:(5,6)(7,5)(3,3)(2,4)(1,7)


解题思路很好:

用value[i] count[i] 分别表示 第i段的数值 和 出现次数;

num[p] left[p] right[p]分别表示位置p所在段的编号和左右端点的位置。

每次查询(left,right)的结果为以下三个部分的最大值:从left到left所在段结束处的元素个数、从right所在段开始到right处的元素个数、中间第num[left]+1段到第num[right]-1段的count的最大值。

特殊情况:如果left和right在同一段中,答案是R-L+1。


解决方法如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 100001;

int n,q;
int d[maxn][35];
int a[maxn];
int value[maxn],count_[maxn];
int num[maxn],left[maxn],right[maxn];

void RMQ_int(){
    for(int i=0;i<n;i++) d[i][0]=count_[i];
    for(int j=1; (1<<j)<=n; j++)
        for(int i=0; i+(1<<j)-1<n; i++)
            d[i][j]=max(d[i][j-1],d[i+(1<<j-1)][j-1]);
}
int RMQ(int L,int R){
    int k=0;
    while((1<<(k+1))<=(R-L+1)) k++;
    return max(d[L][k],d[R-(1<<k)+1][k]);
}
int main()
{
    while(scanf("%d",&n)!=EOF){
        if(n==0) break;
        scanf("%d",&q);
        memset(count_,0,sizeof(count_));
        memset(num,0,sizeof(num));
        memset(left,0,sizeof(left));
        memset(right,0,sizeof(right));
        memset(d,0,sizeof(d));
        for(int i=0;i<n;i++)
            scanf("%d",&a[i]);
        int temp = a[0];
        int t=0;
        value[t]=temp;count_[t]++;
        num[0]=t;left[0]=0;
        for(int i=1;i<n;i++){
            if(a[i]==temp){
                count_[t]++;
                num[i]=t;left[i]=left[i-1];
            }
            else{
                for(int j=left[i-1];j<i;j++){
                    right[j]=i-1;
                }
                temp = a[i];
                t++;
                value[t] = temp;count_[t]++;
                num[i]=t;left[i]=i;
            }
        }
        t++;
        for(int i=left[n-1];i<n;i++){
            right[i]=n-1;
        }

        RMQ_int();
        int left_,right_;
        int ans=0;
        for(int i=0;i<q;i++){
            scanf("%d%d",&left_,&right_);
            left_--;right_--;
            if(num[left_]==num[right_]) ans=right_-left_+1;
            else{
                ans=max(right[left_]-left_+1,right_-left[right_]+1);
                if(num[left_]+1 > num[right_]-1) ans=max(ans,0);
                else{
                    ans=max(ans,RMQ(num[left_]+1,num[right_]-1));
                }
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值