hdu 3183 A Magic Lamp(RMQ或单调队列)

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;
}


 

 


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值