贪心算法
贪心算法通过一系列的选择来得到问题的解。它所做的每一个选择都是当前状态下局部的最好选择,即贪心选择。
贪心选择的一般特征:贪心选择性质和最优子结构性质。
贪心选择性质:
所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。在动态规划算法中,每步所做的选择往往依赖于相关子问题的解。因而只有在解出相关子问题后,才能做出选择。而在贪心算法中,仅在当前状态下做出最好选择,即局部最优选择。然后再去解出做出这个选择后产生的相应的子问题。贪心算法所做的贪心选择可以依赖于以往所做过的选择,但决不依赖于将来所做的选择,也不依赖于子问题的解。正是由于这种差别,动态规划算法通常以自底向上的方式解各个问题,而贪心算法则通常以自顶向下的方式进行,以迭代的方式做出相继的贪心选择,没做一次贪心选择就将所求问题简化为规模更小的子问题。
对于一个具体问题,要确定它是否具有贪心选择性质,必须证明每一步所做的贪心选择最终导致问题的整体最优解。
最优子结构性质:
当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。
弹性算法与动态规划算法的差异:
贪心算法和动态规划算法都要求问题具有最优子结构性质,这是两类算法的一个共同点。大多数时候,能用贪心算法求解的问题,都可以用动态规划算法求解。但是能用动态规划求解的,不一定能用贪心算法进行求解。
适用范围:
贪心算法并不能总求得问题的整体最优解。但对于某些问题,却总能求得整体最优解,这要看问题时什么了。只要能满足贪心算法的两个性质:贪心选择性质和最优子结构性质,贪心算法就可以出色地求出问题的整体最优解。即使某些问题,贪心算法不能求得整体的最优解,贪心算法也能求出大概的整体最优解。如果你的要求不是太高,贪心算法是一个很好的选择。最优子结构性质是比较容易看出来的,但是贪心选择性质就没那么容易了,这个时候需要证明。证明往往使用数学归纳法。
POJ1328
/**最少雷达覆盖全部岛屿问题,通过圆的方程求解每一个岛屿在海岸线上可放置雷达的区间,区间上每一个雷达点均可以覆盖对应的小岛。类似活动选择
从左往右看,区间关系有以下三种情况
(1)若两个区间不相交,则需要两个雷达,每个区间安装一个
(2)若区间有公共部分,则需要一个雷达,按照在左区间的右端点
(3)若一个区间在另一个区间内,则需要一个雷达,按照在小区间的右端点
出现的细节问题:d<=0 时,输出-1; 位置点不一定是整数,可能为浮点数
memory 252K time 63MS*/
# include <cstdio >
# include <iostream >
# include <cmath >
# include <cstdlib >
using namespace std;
struct rec
{
float x,y,left,right;
};
int cmp ( const void *a, const void *b)
{
struct rec *c = (rec *)a;
struct rec *d = (rec *)b;
if(c - >left != d - >left) return (c - >left > = d - >left) ? 1 : - 1;
else return (c - >right > = d - >right) ? 1 : - 1;
}
struct rec in[ 1002];
int n,d,_case,re;
int main()
{
for(_case = 1; ; _case ++)
{
re = 0;
int t,i; double t1;
cin >> n >> d;
if( !n && !d) break;
for (i = 0; i <n; i ++)
{
cin >> in[i].x >> in[i].y;
//以小岛坐标为圆心,依据一元二次方程,求出与x 轴相交区间
t = ( int)(d *d - in[i].y *in[i].y);
if(t < 0 || d < = 0)re = - 1;
else
{
in[i].left =in[i].x -sqrt(( double)t);
in[i].right = in[i].x + sqrt(( double)t);
}
}
if (re != - 1)
{
qsort(in,n, sizeof(in[ 1]),cmp); // 按左节点对结构体按小到大排序
// 从左往右贪心选择雷达位置,每一次保证一个雷达尽可能覆盖更多的小岛
t1 =in[ 0].right;
for(i = 1; i <n; i ++)
{
if( in[i].left < =t1 )
{
if(in[i].right <t1) t1 = in[i].right;
}
else re ++,t1 =in[i].right;
}
if (i ==n)re ++;
}
cout << "Case " << _case << ": " <<re <<endl;
}
return 0;
}