~~~第一次写博客,写得不好请见谅~~~
这是一个非常基础的算法,最适合我这种因为期末考几个星期没有碰代码的人了。为什么写贪心?原因如下:1.老师让我们坚持写博客,所以打算从易到难,先写容易的算法;2.最近做题多数(三分之一)都是贪心;3.来源于一个故事:
(那是一个刚成为oier不久的晚上……)
A同学:(指着我刚AC的一个贪心题)这题怎么做?
我:(不假思索)贪心。
A同学:贪心是什么?我一直没有弄清楚,和dp有什么区别吗?
(我陷入了沉思……是啊,什么是贪心呢?)
用我的话理解,就是局部最优解,即是整体最优解吧!也就是说你并不需要考虑全局,只用考虑此刻!
这岂不是美哉?所以说,贪心一般时间复杂度低,码量也少。
但有的人说了,怎么区分是否是贪心算法。我只能说对于简单的贪心,先猜,在稍加在脑海中证明。
来几道最简单的例题:luogu合并果子 , (刚做的)CF Pair Programming:
首先是合并果子,这是很早以前做的一个题了……
简单概括题意:一共有n堆果子,每堆果子ai个,合并ai堆和aj堆所需要的代价是ai+aj,现在将这堆果子合并成一堆的最小代价是?
不难得出,一共要合并n-1次,在这n-1次里,为了让我们最后的代价最小,目前当然要合并最少的两堆。那我们合并完了就不管了吗?当然不是,我们还要把它塞回到队列中去。于是,这题我便用了堆来维护队列的最小值。
代码如下:
#include<bits/stdc++.h>
using namespace std;
int a[11000],al=0;//a是小根堆,al是它的长
int n;
void ins(int w)
{
a[++al]=w;
int now=al;
while(now!=1)
{
if(a[now/2]>a[now])swap(a[now/2],a[now]);
else break;
now/=2;
}
}//插入
int pop()
{
swap(a[1],a[al]);
int now=1;
while(now*2<al)
{
int c=now*2;
if(a[c]>a[c+1]&&c+1<al)c++;
if(a[c]<a[now])swap(a[c],a[now]);
else break;
now=c;
}
al--;
return a[al+1];
}//弹出,当时还有手打堆得习惯
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
ins(x);//把每一堆果子丢进a数组中
}
int ans=0;
for(int i=1;i<n;i++)
{
int x=pop();
int y=pop();//弹出两个最小值
x+=y;//将它们相加
ans+=x;//记录一下代价
ins(x);//把新的一堆塞回到数组中去
}
printf("%d",ans);
return 0;
}
之后是CF的一道(大概无翻译)
题意大概是:有两个人共享一份文件,最开始有k行。A进行了n个操作[a1,a2,a3……,an],B进行了m个操作[b1,b2,b3……,bn]。A和B要严格按照从左到右的顺序操作,每次可以A和B任意一个人操作。操作0表示我要写一行新的,操作i(0<i<=n+m)表示修改之前的第i行。当然,如果修改的i行大于现有的行数,是不存在的。请输出其中一种合法的步骤。
规定从前往后的顺序,则可以得出一个尽可能多的增加行数的结论。当然,当前合法的我们也可以操作。最后尽可能把不合法的留到最后,因为到那时总行数绝对是最多的(总行数不会减小只会增加)
因此代码如下:
#include<bits/stdc++.h>
using namespace std;
int T,n,m,k,a[11000],b[11000],ans[11000],t;
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&k,&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=m;i++)scanf("%d",&b[i]);
int l=1,r=1;t=0;//l为当前枚举到a数组的下标,r为b数组下标,从前往后枚举
bool p=0;//用来判断合不合法的
for(int i=1;i<=n+m;i++)
{
if((a[l]<b[r]||r>m)&&l<=n)
{
if(a[l]>k){p=1;break;}//无法修改比当前总行数更大的行
else{ans[++t]=a[l];l++;}
if(a[l-1]==0)k++;//上一步l已经加过一个1了,所以是“l-1”
}
else
{
if(b[r]>k){p=1;break;}
else{ans[++t]=b[r];r++;}
if(b[r-1]==0)k++;
}
}
if(p)printf("-1");//不合法输出-1
else for(int i=1;i<=t;i++)printf("%d ",ans[i]);
printf("\n");
}
return 0;
}
以后可能大概放假期间会每周一更,所以打算以一个简单一点的开个头(否则后面都不知道该怎么写)