HDU-3183——A Magic Lamp(RMQ问题+鸽巢原理)

题意:给你一个共n位的数,让你删除其中的m个位,而且不改变原来数位的顺序,使得余下的数位组成的数最小!

分析:大多数人看到标题上的鸽巢原理肯定会疑惑,鸽巢原理是什么屌定理!然后仔细一看必然大失所望,原来鸽巢原理就是抽屉原理。其实这里是巧用了抽屉原理。

我们不妨反过来考虑,要删除m个位,就相当于要保留n-m个位使得组成的数最小,然后就是使得这个数第i(1,2,3,4,5,......)位最小,而且因为先后顺序不能改变(如果可以改变,那么这题就变成排序题了),那么我们先只能先考察第一个数位。我们显然发现第一个数位只能是在给定数位的第1个到第m+1个中的最小数位值的那个,不妨设取得的数位是第k个,那么第二个数位只能在给定数位的第k+1个到第m+2个之中,之后以此类推!最后把前置0去掉就AC了!

话说RMQ问题的ST算法真是好啊!

#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <bitset>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstdlib>
#include <sstream>
#include <cstring>
#include <iostream>
#include <algorithm>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

#define clr(x,y) memset(x,y,sizeof(x))
#define maxn 1000+5
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define IT iterator

typedef long long ll;
const double eps = 1e-9;
const double pi  = acos(-1);
const ll mod = 1e9+7; 


char str[maxn];
char ans[maxn];
int minn[maxn][10];
int min(int i,int j)
{
	return str[i]<=str[j]?i:j;
}
void rmq(int n)
{
	clr(minn,0x3f3f3f3f);
	for(int i=0;i<n;i++)
		minn[i][0]=i;
	for(int j=1;j<10;j++)
		for(int i=0;i<n&&i+(1<<j)-1<n;i++)
			minn[i][j]=min(minn[i][j-1],minn[i+(1<<j-1)][j-1]);
}
int query(int l,int r)
{
	int k=(int)(log(r-l+1.0)/log(2.0));
	return min(minn[l][k],minn[r-(1<<k)+1][k]);
}
int main()
{
	int m;
	while(~scanf("%s %d",str,&m))
	{
		int n=strlen(str);
		rmq(n);
		int ll=-1,M=m;
		int len=n-m;
		for(int i=0;i<len;i++,M++)
		{
			ll=query(ll+1,M);
			ans[i]=str[ll];
		}
		int kk=0;
		while(ans[kk]=='0'&&kk<len)
			kk++;
		if(kk==len)
		{
			puts("0");
			continue;
		}
		for(;kk<len;kk++)
			printf("%c",ans[kk]);
		puts("");
	}
	return 0;
}


你可能感兴趣的:(RMQ,抽屉原理)