超级传送门:http://acm.hdu.edu.cn/showproblem.php?pid=3183
题意要删除m个数字,我们不妨是认为只能挑选n-m个数字,使得有序组合得到的数字最小。
我们先来挑第一个,范围是原数的1~n-(n-m-1)即1~m+1 (注意要给后面的数字至少每个保留一个位置,此处下标从1开始计)。
当然我们要在此区间里挑一个最小的数,假设这个数的位置是x,则接下来第二个数的挑选区间变为[x+1, m+2] ... 往后同理。
转换为RMQ问题,代码如下:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <climits>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int maxn = 1005;
char a[maxn];
char dp[maxn][20];
int val[maxn][20];
char _Min(char a, char b)
{
char _a = a;
char _b = b;
return _a < _b ? _a : _b;
}
void buildST(int n)
{
for (int i = 0; i < n; i++)
{
dp[i][0] = a[i];
val[i][0] = i;
}
for (int j = 1; j < 20; j++)
{
for (int i = 0; i < n; i++)
{
if (i + (1 << j) > n)
break;
dp[i][j] = _Min(dp[i][j - 1], dp[i + (1 << j - 1)][j - 1]);
if (dp[i][j] == dp[i][j - 1])
val[i][j] = val[i][j - 1];
else
val[i][j] = val[i + (1 << j - 1)][j - 1];
}
}
}
int RMQ(int l, int r)
{
int k = floor(log(r - l + 1) / log(2));
int x = _Min(dp[l][k], dp[r - (1 << k) + 1][k]);
if (x == dp[l][k])
return l * 100 + k;
return (r - (1 << k) + 1) * 100 + k;
}
int main()
{
int x;
while (scanf("%s%d", a, &x) > 0)
{
int n = strlen(a);
buildST(n);
int l = 0;
int first = 1;
for (int i = 0; i < n - x; i++)
{
int ret = RMQ(l, x + i);
int index2 = ret % 100;
int index1 = (ret - index2) / 100;
l = val[index1][index2] + 1;
if (first && dp[index1][index2] == '0')
continue;
printf("%c", dp[index1][index2]);
first = 0;
}
if (first)
printf("0");
printf("\n");
}
return 0;
}