HDU3183(RMQ问题,ST算法)

题目: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 1005

int 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;
}


 

你可能感兴趣的:(HDU3183(RMQ问题,ST算法))