贪心算法是很多经典算法的基本思想,比如kruskal和dijkstra
贪心算法一般用来求解最优化问题的,首先要清楚一个概念:
最优子结构性质:
如果问题的最优解由相关子问题的最优解组合而成,并且这些子问题可以独立求解,那么称这种问题满足最优子结构性质。
贪心算法的设计步骤##
摘自算法导论:
1.将最优化问题转化为这样的形式:对其做出一次选择后,只剩下一个子问题需要解决。
2.证明做出贪心选择后,原问题总是存在最优解,即贪心选择总是安全的。
3.证明做出贪心选择后,剩余的子问题满足性质:其最优解与贪心选择组合即可得到原问题的最优解,这样就得到了最优子结构如何证明一个贪心算法是否能求解出一个最优化问题呢?并没有适合所有情况的方法,但贪心选择性质和最优子结构是两个关键要素。如果我们能证明问题具有这些性质,就向贪心算法迈出了重要的一步。
一个动态规划算法是自底向上进行计算的,而贪心算法是自顶向下的,进行一次又一次选择,将给定问题变得更小。
在找得到了一个贪心策略后如何证明这个策略的正确性呢?一个比较常见的套路是反证法:证明如果不这样做我们的结果不会变得更优。
例如最小生成树的kruskal算法中“按照边的权值从小到大在不成环的前提下不断加边”这一策略。假设总的边集为
E
E
E,已经选择的边集为
E
′
E'
E′,这一策略的形式化描述就是在
E
−
E
′
E-E'
E−E′中选择最小的且不会成环的边加入到
E
′
E'
E′中,为什么这一策略是对的呢?按照上面的反证法,假设不加入这条边(
E
−
E
′
E-E'
E−E′中权值最小的边),那么在以后的假想的最小生成树中再加入这条边(一定会形成环)并取消环上比这条边大的边,这样结果只会变得更优。所以我们就证明了如果不这样做我们的结果不会更优。
贪心往往要结合一定的排序策略
例题一
- 题目大意
有n个节目,每个节目有一个开始时间s和结束时间t,小明一个时间只能看一个节目,问小明最多能看多少个节目。
-
分析
策略是:按照结束时间从小到大将节目进行排序,依次判断每个节目是否能看,若能则把它加入到看的节目中再判断下一个节目。
为什么这样做是正确的呢?采用反证法:假设当前可以加的节目我们没有加入进去。那么在最优的方案中我们总可以通过取消一个节目将这个节目加入进去 -
代码
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<algorithm>
using namespace std;
int n;
int ans;
struct LINE
{
int a;
int b;
}line[105];
bool cmp(const struct LINE &x,const struct LINE &y)
{
return x.b < y.b;
}
int main()
{
while(scanf("%d",&n)!=EOF && n)
{
ans=0;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&line[i].a,&line[i].b);
}
sort(line+1,line+n+1,cmp);
int cur=0;
for(int i=1;i<=n;i++)
{
if(line[i].a>=cur)
{
ans+=1;
cur=line[i].b;
}
}
printf("%d\n",ans);
}
}