贪心 -- 局部最优解 -> 全局最优解
Introduction
博才
这边的教练要求巩固基础算法,然而我怀疑我买的一本通是本上的例题的假书
代码
是假的
,而且例题更是没有输入输出格式
,所以,不管怎样我是不会按上面来的一定会做题
的
然后我没有了这个条件,转而看向竞赛宝典,本着巩固提高的方针看了看贪心
Body
还是一道例题理解一下:
题面
现在有一个数字\(N\),要求去掉\(S\)个数字,使这个数字去掉\(S\)个数字后最小,求去掉后的数字最小是多少?
输入格式
一行,一个数字\(N\),去掉数字的个数\(S\),中间用空格隔开
输出格式
一行,一个数字,表示去掉\(S\)个数字后的数字
样例输入
314 1
样例输出
14
数据范围
\[0<=S<N<=1e10000\]
首先,因为我们知道贪心
是一个局部最优解->全局最优解
的算法,所以相当于我们可以把每个步骤求一遍最优解然后继续求解,所以这个时候我们就可以考虑贪心具体的步骤了
现在请读者自己思考思考,答案在下方
想出来了吗?考虑到有一些Markdown编辑器
的不支持的缘故,不采用字体颜色为白
和注释
的做法请谅解
答案:
简单的从高位向低位,然后如果递减,那么选择最前面的一个,也就是该区间最大一个数字,如果递增,则选择最后一个,也就是该区间最大的一个数字
Code:
#include<cstdio>
#include<cstring>
using namespace std;
#define maxn 10005
char n[maxn];
int len;
inline void Delete_num(int num)
{
for(int i=num;i<len-1;i++) n[i]=n[i+1];
len--;
}
int main()
{
scanf("%s",n);
int s;
scanf("%d",&s);
len=strlen(n);
int now=0;
int st=0;
int ans=s;
bool book=0;
bool flag=0;
while(ans)
{
for(int i=0;i<len;i++)
{
if(i==0)
{
if(n[i]==48) {Delete_num(i);flag=1;break;}
continue;
}
if(n[i]<n[i-1]||(n[i]==n[i-1]&&now==1))
{
if(!now) now=1;
if(now==1) continue;
if(now==2) {Delete_num(i-1);ans--;book=1;now=0;break;}
}
if(n[i]>n[i-1]||(n[i]==n[i-1]&&now==2))
{
if(!now) now=2;
if(now==1){ Delete_num(st);ans--;book=1;now=0;break;}
if(now==2) continue;
}
}
if(flag){flag=0;continue;}
if(book){book=0;continue;}
if(now==1){Delete_num(st);ans--;continue;}
if(now==2){Delete_num(len-1);ans--;}
}
for(int i=0;i<len;i++) printf("%c",n[i]);
return 0;
}
中间的删除代码其实就是用时间换空间,如果你想要更少的时间,事实上你可以选择用空间换时间,空间大小为10005
即可,所以用空间换时间是一个不错的选择,但是由于作者比较懒惰,所以还请读者自主思考
接下来还是讲题:
题面
给定一个由正整数组成的数列,要求你用合并操作合成一个数字,h合并过程如下:
给定两个数字a
,b
;有合并之后的数字c=a*b+1
;
给定一个数列,求它的极差,也就是最大值和最小值之差
输入格式
两行,第一行一个正整数n
第二行n
个正整数表示一个序列
输出格式
一行,一个数字表示极差
数据范围和解释
\(1 \leqslant n \leqslant 100000\)
\(1 \leqslant a_i \leqslant 2147483647(Maxint)\)