RMQ: 计算min{A(L),A(L+1),...A(R)} 常用ST算法
令d(i,j)表示从i开始的,长度为2^j的一段元素的最小值,则可以递推出d(i,j)= min { d(i, j-1) , d(i+2^(j-1),j-1)}
因此预处理所花费的时间为NlogN,
查询的时候令K为符合2^K<= R-L+1条件的最大整数 则最小值=min{d[L][K], d[R-(1<<K)+1][K]}
查询所花费的时间为O(1)
这题在长度为len的整数中去除K位整数使得最后的整数最小,也就是从长度为len的整数中选取L-K位整数出来
第一位整数可选取的范围为【1,K+1】,设最后选取的是第L1为
则第二位整数可选取的范围为【L1+1, K+2】。。。。
因为要求出选取的最小值是序列中的第几位,所以此时d数组内应该存储下标。。
代码:
#include <stdio.h>
#include <string.h>
int s[1005];
int min(int x, int y)
{
return s[x]<= s[y]? x: y;
}
int main()
{
char ch[1005];
int k;
while(scanf("%s %d",ch+1,&k)!=EOF)
{
int l= strlen(ch+1);
for(int i= 1; i<= l; i++)
s[i]= ch[i]-'0';
int dp[1001][11];
for(int i= 1; i<= l; i++)
dp[i][0]= i;
for(int j= 1; (1<< j)<= l; j++)
for(int i= 1; (i-1)+(1<<j)<= l; i++)
dp[i][j]= min(dp[i][j-1], dp[i+(1<<(j-1))][j-1]);
int a[1005];
a[0]= l- k;
int flag= 1;
for(int i= 1; i<= l-k; i++)
{
int L= flag;
int R= k+i;
int m= 0;
while(1<<(m+1)<= R- L+1)
m++;
flag= min(dp[L][m], dp[R-(1<<m)+1][m]);
a[i]= s[flag];
flag++;
}
int pre= 0;
for(int i= 1; i<= a[0]; i++)
if(pre)
printf("%d",a[i]);
else if(a[i])
{
printf("%d",a[i]);
pre= 1;
}
if(!pre)
printf("0");
printf("\n");
}
return 0;
}
单调队列的解法:
设序列的长度为L,需去除的元素个数为N,当A[i]<A[i+1] 时,去掉A【i】可以使结果更优
所以我们可以用单调队列去维护,使得队列中的前L-N个元素一定为最优解
代码:
#include <cstdio>
#include <cstring>
#define maxn 1005
int main()
{
char ch[maxn];
int num[maxn];
int A[maxn];
int n;
while(scanf("%s %d",ch+1, &n)!=EOF)
{
int L= strlen(ch+1);
int sum= L- n;
for(int i= 1; i<= L; i++)
num[i]= ch[i]-'0';
if(L == 1)
{
if(n== 1)
printf("0\n");
else
printf("%d\n",num[1]);
continue;
}
int S= 1;
A[1]= num[1];
A[0]= 10;
for(int i= 2; i<= L; i++)
if(n)
{
if(num[i]>= A[S])
{
S++;
A[S]= num[i];
}
else
{
int j= S;
while(n && j && A[j]> num[i]) // &&j 防止队列清空之后还是继续清理
{
n--;
j--;
}
S= j+1;
A[S]= num[i];
}
}
else
{
S++;
A[S]= num[i];
}
int flag= 0;
for(int i= 1; i<= sum; i++)
if(flag)
printf("%d",A[i]);
else
{
if(A[i])
{
flag= 1;
printf("%d",A[i]);
}
}
if(!flag)
printf("0");
printf("\n");
}
return 0;
}