一、贪心算法
解决最优化问题的算法一般包含一系列的步骤,每一步都有若干的选择。对于很多最优化问题,只需要采用简单的贪心算法就可以解决,而不需要采用动态规划方法。贪心算法使所做的局部选择看起来都是当前最佳的,通过局部的最优化选择来产生全局最优解。本文将介绍贪心算法的理论基础和一些简单应用。在求最优解问题的过程中,依据某种贪心标准,从问题的初始状态出发,直接去求每一步的最优解,通过若干次的贪心选择,最终得出整个问题的最优解,这种求解方法就是贪心算法。
设计贪心算法的步骤:
将最优化问题转化为这样一个问题,即先做出选择,再解决剩下的一个子问题。
证明原问题的最优解之一可以由贪心选择得到。
证明在做出贪心选择后,将剩余的子问题的最优解和我们所做的贪心选择组合起来,可以得到原问题的一个最优解。
一般,如果我们能证明一个问题具有以下两个关键的性质,那么就可以设计出它的一个贪心算法。
(1)贪心选择性质:一个全局最优解可以通过局部最优解(贪心)选择来达到。即,当考虑做选择时,只考虑对当前问题最佳的选择而不考虑子问题的结果。而在动态规划中,每一步都要做出选择,这些选择依赖于子问题的解。动态规划一般是自底向上,从小问题到大问题。贪心算法通常是自上而下,一个一个地做贪心选择,不断地将给定的问题实例规约为更小的子问题。
(2)含有最优子结构: 如果问题的一个最优解包含了其子问题的最优解,则称该问题具有最优子结构。**
利用贪心解决问题的关键需要考虑如何选取贪心标准。
贪心算法的理论基础
关于贪心算法有一种理论可用于确定在何时使用贪心算法能给出最优解,虽然这种理论并没有包含贪心算法所适用的所有情况,但确实描述了许多非常有意义的情况。
二、适用题型
1.活动选择问题。
这层楼沿着走廊南北向的两边各有200个房间。最近,公司要做一次装修,需要在各个办公室之间搬运办公桌。由于走廊狭窄,办公桌都很大,走廊里一次只能通过一张办公桌。必须制定计划提高搬运效率。经理制定如下计划:一张办公桌从一个房间移到另一个房间最多用十分钟。当从房间i移动一张办公桌到房间j,两个办公室之间的走廊都会被占用。所以,每10分钟内,只要不是同一段走廊,都可以在房间之间移动办公桌。
思路如果搬运桌子的路径有重叠,那么必定不能够同时进行,所以考虑每个房间经过的次数即为搬运的最短情况。
int i, j;
int move[200];
int N; //搬运次数
//每次搬运的起点和终点
int from, to;
scanf("%d", &N);
memset(move, 0, sizeof(move));
for(i = 0; i < N; i++)
{
scanf("%d%d", &from, &to);
//将房间号映射为走廊号
from = (from - 1)/2;
to = (to - 1)/2;
//确保from<to,C++使用:swap(from, to)
if(from > to) {
int temp = from;
from = to;
to = temp;
}
//占用走廊情况
for(j = from; j <= to; j++)
move[j]++;
}
2.可拆分背包问题
选出贪心准则,按照符合题意程度优先的顺序选择。
例如,去自助餐厅吃饭,要想吃回本就要捡着贵的吃,但是贵只是一方面,人会饱,所以用价格除以质量所获的价格商才是贪心准则,应按照价格商优先进行选取。
3.最优装载问题
有一批集装箱要装上一艘载重量为c的轮船,其中集装箱i的重量为wi。最优装载问题要求确定在装载体积不受限制的情况下,将尽可能多的集装箱装上轮船。
既然体积不受限制,只受质量限制,那可知从质量小的集装箱开始选取。
4.删数问题
string a; //n位数a
int k;
cin>>a>>k;
//如果k≥n,数字被删完了
If (k >= a.size()) a.erase();
else while(k > 0)
{
//寻找最近下降点
int i;
for (i=0; (i<a.size()-1) && (a[i] <= a[i+1]); ++i);
a.erase(i, 1); //删除xi
k- -;
}
//删除前导数字0
while(a.size() > 1 && a[0] == '0')
a.erase(0, 1);
cout<<a<<endl;
5.多处最优服务次序问题
本题想让客户等待时间短,那如果让时间长的在前,就会有所用人等候最长时间人情况出现,肯定不为最优解。假设从最小的开始,人的基数乘以时间是一个小量,然后事间逐渐延长,人数减少。
6.多机调度问题
n个作业组成的作业集,可由m台相同机器加工处理。要求给出一种作业调度方案,使所给的n个作业在尽可能短的时间内由m台机器加工处理完成。作业不能拆分成更小的子作业;每个作业均可在任何一台机器上加工处理。这个问题是NP完全问题,还没有有效的解法(求最优解),但是可以用贪心选择策略设计出较好的近似算法(求次优解)。当n<=m时,只要将作业时间区间分配给作业即可;当n>m时,首先将n个作业从大到小排序,然后依此顺序将作业分配给空闲的处理机。也就是说从剩下的作业中,选择需要处理时间最长的,然后依次选择处理时间次长的,直到所有的作业全部处理完毕,或者机器不能再处理其他作业为止。如果我们每次是将需要处理时间最短的作业分配给空闲的机器,那么可能就会出现其它所有作业都处理完了只剩所需时间最长的作业在处理的情况,这样势必效率较低。
7.区间覆盖问题
POJ1328是一道经典的贪心算法例题。题目大意是假设海岸线是一条无限延伸的直线。陆地在海岸线的一侧,而海洋在另一侧。每一个小的岛屿是海洋上的一个点。雷达坐落于海岸线上,只能覆盖d距离,所以如果小岛能够被覆盖到的话,它们之间的距离最多为d。题目要求计算出能够覆盖给出的所有岛屿的最少雷达数目。对于每个小岛,我们可以计算出一个雷达所在位置的区间。
问题转化为如何用尽可能少的点覆盖这些区间。先将所有区间按照左端点大小排序,初始时需要一个点。如果两个区间相交而不重合,我们什么都不需要做;如果一个区间完全包含于另外一个区间,我们需要更新区间的右端点;如果两个区间不相交,我们需要增加点并更新右端点.