题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3183
A Magic Lamp
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 1763 Accepted Submission(s): 692
The question is: give you an integer, you are allowed to delete exactly m digits. The left digits will form a new integer. You should make it minimum.
You are not allowed to change the order of the digits. Now can you help Kiki to realize her dream?
Each test case will contain an integer you are given (which may at most contains 1000 digits.) and the integer m (if the integer contains n digits, m will not bigger then n). The given integer will not contain leading zero.
If the result contains leading zero, ignore it.
178543 4 1000001 1 100001 2 12345 2 54321 2
13 1 0 123 321
[0,m]这个长度为m+1的区间内,也可以用反证法证明,若第一个数在[m+1,n-1]这个区间内,那之后最多能选取n-1-(m+2)+1=n-m-2,事实上我们还差n-m-1个数,所以,原假设不成立。设取出来的第一个数位置为x,则同理,第二个数必然在[x+1,m+1]这个区间里,同样地第三个数在[x',m+2],,,,一直到最后一个数。现在我们要解决的问题是要在[x'+1,m+i](i对应取第i-1个数)这个区间里取一个最小的数,这就是RMQ问题(Range mini query)。可以用线段树实现,不过基于线段树的RMQ创建的复杂度为o(n*log(n))查询复杂度为o(log(n)),而基于稀疏表(ST)的创建复杂度为o(n*log(n)),但查询复杂度为o(1),是一个在线算法。一般如果对区间有更新的话,我们就用线段树,不更新的话用ST算法比较高效。
ST算法是基于动态规划思想实现的。其状态转移方程为dp[i][j]=min(dp[i][j-1],dp[i+(j-1)^2][j-1]),其dp[i][j]代表从i点起长度为2^j的区间的最小(大)值
而查询的话只需要算出能覆盖它的最小区间的最值就可以了。为min(dp[l][((int)log2(len))],dp[r+1-2^((int)log2(len))][(int)log2(len)]);
具体算法设计分析过程可以参考博文http://blog.csdn.net/liang5630/article/details/7917702,此处不再赘述。
AC代码如下
#include<cstdio>
#include<cmath>
#include<cstring>
const int maxn=100005;
int dp[maxn][30],m;
char s[maxn],ans[maxn];
int Min(int i,int j) { return s[i]<=s[j]?i:j;}
void InitRMQ(int n)
{
for(int i=0;i<n;i++) dp[i][0]=i;
for(int j=1;(1<<j)<=n;j++)
for(int i=0;i+(1<<j)-1<n;i++)
dp[i][j]=Min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
int RMQ(int l,int r)
{
int k=(int)(log((r-l+1)*1.0)/log(2.0));
return Min(dp[l][k],dp[r-(1<<k)+1][k]);
}
int main()
{
while(~scanf("%s%d",s,&m))
{
int x=0,pa=0,n=strlen(s),pb;
InitRMQ(n);//创建ST表
for(int i=0;i<(n-m);i++) x=RMQ(x,m+i),ans[pa++]=s[x++];
for(pb=0,ans[pa]='\0';ans[pb]=='0';pb++);
if(pa==pb) printf("0\n");
else printf("%s\n",ans+pb);
}
return 0;
}