HDU3183(RMQ问题,ST算法)

来源:互联网 发布:vps绑定阿里域名 编辑:程序博客网 时间:2024/05/17 12:49

题目:A Magic Lamp

 

题意:

对于一个序列A[1...N],一共N个数,除去M个数使剩下的数组成的整数最小。

也就是说在A[1...N]中顺次选取N-M个数,使值最小。

本题很有技巧性,一开始我总是想不明白,后来在纸上画了一下,大概明白了是怎么回事。

它主要是基于以下事实:

对于序列A[1...N],选取N-M个数,使组成的值最小,而且顺序不能交换,既然要选取N-M个,那么可以容易知道这N-M位数的第一位一定在数组A中的区间

[1M+1]中出现,为什么是这样呢?

我们可以这样来模拟一下:假设A数组就只有6个数,分别是A[1]A[2]A[3]A[4]A[5]A[6],我们去掉M=2个数,使形成的值最小。

那么我们此时的N=6,M=2N-M=4

则我们说形成的4位数的第一位一定在区间[13]中出现,因为如果区间范围再大点,比如[14],这样就不科学了,因为第一位一定不会是A[4],更不会是

A[5]A[6],我们假设可以是A[4],那么后面只有A[5]A[6]两位数了,这样的话最多只可能形成3位数,绝对不能形成N-M=4位了。

当然到了这里,我们就可以这样做了,第一位可以在区间[1M+1]里面找,假设第一位在位置x,因为第二位肯定在第一位的后面,所以第二位一定存在于

区间[x+1M+2],为什么是M+2,因为第一位已经确定了,现在只需要确定N-M-1位了,所以区间就可以向后增加1,一直这样循环下去,就可以找到了。

 

#include <stdio.h>#include <string.h>#include <math.h>#define N 1005int m,n;char a[N];char num[N];int f[N][N];int min(int i,int j){    return a[i]<=a[j] ? i:j;}void ST(){    int i,j;    for(i=0;i<n;i++)       f[i][0]=i;    for(j=1;j<=(int)(log((double)n)/log(2.0));j++)    {        for(i=0;i+(1<<j)-1<n;i++)           f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);    }}int query(int L,int R){    int x=(int)(log(double(R-L+1))/log(2.0));    return min(f[L][x],f[R-(1<<x)+1][x]);}int main(){    int i,j,L,R;    while(~scanf("%s%d",a,&m))    {        int len=strlen(a);        n=len;        m=len-m;        ST();        i=j=0;        while(m--)        {            i=query(i,len-m-1);            num[j++]=a[i++];        }        for(i=0;i<j;i++)        {            if(num[i]!='0')               break;        }        if(i==j)        {            puts("0");            continue;        }        while(i<j)        {            printf("%c",num[i]);            i++;        }        puts("");    }    return 0;}


 

原创粉丝点击