HDU 3183 A Magic Lamp RMQ

题意:

给一串不含前导零的\(n\)个数字,要删去\(m(m \leq n)\)个数字,要使剩下的数字最小。

分析:

删去\(m\)个数字就相当于选\(n-m\)个数字,因为最终选出来的数字的长度是一定的,所以第一个数字越小越好。
第一个数字只能在区间\([1,m+1]\)中选,否则后面的就不够选了。
假设我们选在了位置\(p\),那么第二个数字就在区间\([p+1, m+2]\)中选,以此类推。

因此RMQ中我们要查询的是最小值的最左下标。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxn = 1000 + 10;

char s[maxn], ans[maxn];
int n, m;

int d[maxn][11];

int Min(int i, int j) { return s[i] <= s[j] ? i : j; }

void init() {
    for(int i = 0; i < n; i++) d[i][0] = i;
    for(int j = 1; (1 << j) <= n; j++) {
        for(int i = 0; i + (1<<j) - 1 < n; i++) {
            d[i][j] = Min(d[i][j-1], d[i + (1<<(j-1))][j-1]);
        }
    }
}

int query(int L, int R) {
    int k = 0;
    while((1 << (k+1)) <= R - L + 1) k++;
    return Min(d[L][k], d[R-(1<<k)+1][k]);
}

int main()
{
    while(scanf("%s", s) == 1) {
        n = strlen(s);
        scanf("%d", &m);
        init();

        int choose = -1;
        for(int i = 0; i < n - m; i++) {
            choose = query(choose + 1, m + i);
            ans[i] = s[choose];
        }
        ans[n - m] = 0;

        int st;
        for(st = 0; st < n - m; st++) if(ans[st] > '0') break;
        if(st >= n - m) printf("0\n");
        else printf("%s\n", ans + st);
    }

    return 0;
}
posted @ 2015-11-26 22:37  AOQNRMGYXLMV  阅读(131)  评论(0编辑  收藏  举报