之前接触过背包问题和动态规划,对贪心算法和其区别做过相关的总结,即便如此,遇到这一类问题还是稍微有点懵逼,所以本文的主旨在于阐述贪心算法中简单贪心的一些基本概念以及和动态规划的具体区别。
一、简单贪心的基础概念:
贪心算法就是求解一类最优化问题的方法,其主要考虑当前情况下局部状态的最优解,从而使得全局结果达到最优。对于简单贪心的证明,我们可以使用反证法和数学归纳法给予证明,这里不做过多阐述。在PAT 乙级考试中,B1020题就给出了类似场景,并且看起来就是一个背包问题。这里注意下,背包问题都可以使用动态规划求解,但是贪心算法只能解决部分背包问题。
二、区间贪心问题:
区间贪心问题可以视为贪心问题的一种应用问题(但是说句实话,个人并没有很好的体会到其中的贪心算法的思想),其有具体的描述;
区间贪心问题也就是区间不想交问题,也就是对于N个开区间(x,y),从中选择尽可能多的开区间,使得两两并无交集。
其主要的解决思路是判定两种基本情况:
1.当某区间a是另一个区间b的子集时,我们通常选择a,原因是可以给出更大的空间去容纳其他空间;
2.将所有开区间按照左端点x进行大小排序,在去除区间包含的情况下,一定会有各个区间右端点从大到小排列的情况(这也就是第一步去除包含区间的必然结果,也可以视为原因),排列情况如下图所示。可以看出,I1的右边一段不可能会重叠,左端也会被I2进行包含,所以我们选择I1。所以针对第二种情况,我们选用左边端点最大。
对于情况2,总是选择右端点最小的区间也是可行的,但是个人有一个疑问,现在没有解决。左边右边貌似都不能保证选择后区间最大。这个问题还有待后续完全版贪心算法的补充;
书上附带的理解代码如下,虽然只是区分了单独区间,但是依然分了两类情况:
#include<stdlib.h>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=110;
struct Inteval{
int x,y;
}I[maxn];
bool cmp(Inteval a,Inteval b){
if(a.x!=b.x)
return a.x>b.x;//左端从大到小进行排序
else
return a.y<b.y;//有段从小到大进行排序
}
int main(){
int n;
while(scanf("%d",&n),n!=0){
for(int i=0;i<n;i++){
scanf("%d%d",&I[i].x,&I[i].y);
}
sort(I,I+n,cmp);
int ans=1,lastX=I[0].x;
for(int i=1;i<n;i++){
if(I[i].y<=lastX){
//该情况下为不相交区间
lastX=I[i].x;
ans++;
}
}
}
system("pause");
}
还有特别经典的是区间点问题,是建立在贪心区间的基础之上。
该问题衍生出来的题目也是贪心问题中比较经典的非洲人洗澡问题。贪心区间点的详细描述是在一堆闭区间点内,如何选择最少的点,使得每一个区间都包含有一个点。
该问题建立在区间问题之上,也是分为两种情况,第一种重合区间只需要在子区间布点。第二种排序后的区间则选择另一端的点,来保证选取一个点,就可以在连续区间的内所有的所有区间内部。