题意:给定一个整数,可以删除m个数字,求原整数删除m个数字后变成的数最小是多少。
解法1:设整数的长度为 n ,则删除完成之后的数的位数为 n-m,且无论如何删除完成之后的整数 的最高位肯定是原整数[0,m]区间中的一位,因为要求最小,所以第一位肯定是[0,m]区间中最小的一位,然后继续往后推就行了,相同的数肯定优先选择左边的,因为这样下一步可供选择的区间会比较大。区间最大值就用RMQ维护一下
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
const int N = 1000+10;
int m,n;
char s[N];
char ans[N];
int f[N][15];
int Min(int i,int j) { return s[i]<=s[j]?i:j; }
void init()
{
memset(f,0,sizeof(f));
for(int i=0;i<n;i++) f[i][0]=i;
for(int k=1;(1<<k)<=n;k++)
for(int i=0;i+(1<<k)<=n;i++)
f[i][k]=Min(f[i][k-1],f[i+(1<<(k-1))][k-1]);
}
int query(int L,int R)
{
int k=0;
while((1<<(k+1))<=R-L+1) k++;
return Min(f[L][k],f[R-(1<<k)+1][k]);
}
int main()
{
while(~scanf("%s%d",s,&m))
{
n=strlen(s);
int cnt=n-m;
init();
int now=0,tot=0;
while(cnt--)
{
now=query(now,n-cnt-1);
ans[tot++]=s[now++];
}
int flag=0;
for(int i=0;i<tot;i++)
{
if(ans[i]=='0'&&flag==0) continue;
flag=1;
putchar(ans[i]);
}
if(flag==0) putchar('0');
puts("");
}
return 0;
}
解法2:不断从当前序列中找到从左到右第一个非递减连续序列的最后一位并删除,可以证明这样贪心是最优的,所以可以用单调队列维护求解,复杂度较上面会更优。