技巧:给你一个数,要求你删去\(m\)位使得剩下的数最大
这是一个贪心问题,假设原来的数字是\(k\)位,那么相当于要保留\(k-m\)位。
有下面几种贪心策略
\(1.\)每次找最大的保留,直到\(k-m\)个,这样显然是错的,因为要求删除后顺序不能改变。
\(2.\)找到最大的且最靠前的位置,保留它,再从它后面这样操作,直到选够\(k-m\),这样显然也是错的,假如要保留\(4\)位,数据为\(123987\),直接选取\(9\)没办法凑够\(k-m\)个
于是有了下面这种贪心:
设\(L=1,R=m+1\)
考虑\(1-m+1\)位必须选取一个,如果不选取那么后面的\(k-m-1\)位都选也凑不够\(k-m\),所以在前\(1-m+1\)位选取一个最大的数,记他的位置为\(i\),将它加入答案,然后让\(L=i+1,R++\)
这样为什么是对的?
\(1.\)合法性,第一次要在\(1-m+1\)里选择一个数,第二次在\(i+1-m+2\)里选择一个数。每次选择的时候区间\(L-R\)至少有一个数,一定可以选择,每次选择且只选择\(1\)个。如此选了\(m-k\)次,每次右端点\(+1\),一定可以选出长度为\(m-k\)的答案\((1)\);
而且每次选完保证下一次在这个数的后面选,不会出现顺序不合法的情况\((2)\)。
\(2.\)最优性,每个数都会被考虑,比如“第一次要在\(1-m+1\)里选择一个数,第二次在\(i+1-m+2\)里选择一个数”这个过程,根据答案合法性,第一个数一定出自\(1-m+1\),第二个数一定出自\(1-m+2\),所以这样控制区间没有情况会被漏下,且每次选取最大值保证了合法的基础上最优。
总复杂度最坏是\(O(n^2)\),最好是\(O(n)\)
下面是一道相关题目
题面
\(Code\)
#include<cstdio>
#include<iostream>
#include<queue>
#define maxn 30010
#define re register
using namespace std;
priority_queue<int,vector<int>,greater<int> > q;
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int m,k,tmp,CNT,cnt;
int need,t[maxn],a[maxn];
char s[10001000],tcc[10001000];
void get_num(int x)
{
CNT=0;
while(x)
{
tcc[++CNT]='0'+x%10;
x/=10;
}//注意这里是倒序,别弄错了
for(re int i=CNT;i>=1;--i) s[++cnt]=tcc[i];
}
int main()
{
k=read(),m=read();
q.push(1);
for(re int i=1;i<=k;++i)
{
tmp=q.top();
q.pop();
get_num(tmp);
a[i]=tmp;
q.push(2*tmp+1);
q.push(4*tmp+5);
}
int L=1,R=m+1;
for(re int i=1;i<=k;++i) printf("%d",a[i]);
printf("\n");
while(L<=R&&R<=cnt)
{
int maxx=0;
for(re int i=L;i<=R;++i)
{
if((s[i]-'0')>maxx) {maxx=s[i]-'0';L=i+1;}//下次从这后面遍历就行
}
printf("%d",maxx);
R++;//下面这个区间也一定有一个解
}
return 0;
}
原文地址:https://www.cnblogs.com/Liuz8848/p/11712125.html