在求最优解问题的过程中,依据某种贪心标准,从问题的初始状态出发,直接去求每一步的最优解,通过若干次的贪心选择,最终得出整个问题的最优解,这种求解方法就是贪心算法。它所做出的选择只是在某种意义上的局部最优解。
贪心解题考虑问题:
题目是否适合于用贪心策略求解
如何选择贪心标准,以得到问题的最优/较优解
最优子结构:
当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。
问题的最优子结构性质是该问题可用贪心算法或动态规划算法求解的关键特征
贪心算法求解问题
候选集合A:为了构造问题的解决方案,有一个候选集合A作为问题的可能解,即问题的最终解均取自于候选集合A。
解集合S:随着贪心选择的进行,解集合S不断扩展,直到构成满足问题的完整解。
解决函数solution:检查解集合S是否构成问题的完整解。
选择函数select:即贪心策略,这是贪心法的关键,它指出哪个候选对象最有希望构成问题的解,选择函数通常和目标函数有关。
可行函数feasible:检查解集合中加入一个候选对象是否可行,即解集合扩展后是否满足约束条件。
做题时应注意结合题目给出的示例输出,总结贪心规律,然后结合stl和以前学过的知识进行算法设计。
经典例题: ZOJ1029-MOVING TABLES
问题简述:
走廊上共有400个房间,两边各有200个房间,房间之间要搬桌子,走廊每次(同一时间)只能通过一张桌子,每次搬动桌子占用10分钟,问搬动那些桌子最少需要多长时间。
问题分析:
把走廊的各个点编一个号,每次搬运桌子都会占用走廊,针对占用的走廊编号做个统计,再取最大值就可以了,即1号房间和2号房间都对应位置1的走廊,3号房间和4号房间对应位置2的走廊,一共有200个这样的位置。用a[201]定义这些位置,其中某位置的值就是经历这个位置的次数。如果从某一号房到另一号房,所经历的每个位置的次数都+1,最后再全体扫描下哪个位置经历次数最多(贪心规律),输出值便是这个位置的值*10。不要遗忘可能起始房间号比终止房间号大
示例代码:
#include <iostream>
using namespace std;
const int N = 200;
int corridor[N + 1];
int main()
{
int t, n, start, end;
cin>>t;
while(t--) {
memset(corridor, 0, sizeof(corridor));
//*每更新一次数据就清空一次数组*//
cin>>n;
for(int i=1; i<=n; i++) {
cin>>start>>end;
if(start > end)
swap(start, end);
for(int j=(start + 1) / 2; j<=(end + 1) / 2; j++)
corridor[j] += 10;
}
int maxtime = 0;
for(int i=1; i<=N; i++)
maxtime = max(maxtime, corridor[i]);
//*遍历出最多次数*//
cout<<maxtime;
}
return 0;
}
遇到的问题:
很多题目知道贪心策略就是代码不能实现输出格式错误,超时错误,甚至是某一处变量不小心打错就得调试好长时间。针对上述问题没有什么好办法,只能学好编程(多做题),学好英语(变量直接代入),学好数学(核心算法明了)。
想说的话:优秀的人在身边,卓越的人在对面。
自我勉励:路漫漫其修远兮,吾将上下而求索。