贪心
一.思想:就是走一步看一步,每一步选取当前面临的最优选择,但是这样的目光是短浅的
比如说一个二叉树,只能选择一个子树上的数进行相加然后从选择的子树出发,继续向下找,求最大总和
用贪心的思想你会选择19,然后再选择2。事实上这不是最大的,17-4-100的这条路才是最大的,所以贪心是“目光短浅的”
选用贪心需要满足两个特征:
1.最优子结构。就是说,从局部最优能扩展到全局最优
2.贪心选择性质。问题的整体最优解可以通过一系列的局部最优的选择来得到。
二。常见问题
1.活动安排问题
例题hdu-2037
有三种贪心策略:(1)最早开始时间;(2)最早结束时间;(3)用时最少;
第一种策略是错误的,如果一个活动迟迟不终止,后面的活动就无法开始。而第二种策略,一个尽快终止的活动可以容纳更多的活动。第三种显然是错误的。
算法步骤:(1)把n个活动按结束时间排序;(2)选择第一个结束的活动,并删除与他冲突的活动;(3)重复步骤,直到活动为空
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
using namespace std;
struct Node
{
int start;
int end;
}node[200];
bool cmp(const Node& a, const Node& b)
{
return a.end < b.end;
}
int main()
{
int n;
while (~scanf("%d", &n) && n)
{
for (int i = 0; i < n; i++)
scanf("%d%d", &node[i].start, &node[i].end);
sort(node, node + n, cmp);
int count = 0;
int lastend = -1;
for (int i = 0; i < n; i++)
{
if (node[i].start >= lastend)
{
count++;
lastend = node[i].end;
}
}
printf("%d\n", count);
}
return 0;
}
2.区间覆盖问题
给定一个长度为n的区间,再给出m条选段的左端点和右端点,问最少用最少条线段可以将整个区间完全覆盖。
步骤如下:(1)把每个线段按左端点递增排序;(2)设已经覆盖的区间[L,R],在剩下的线段中找所有左端点小于等于R且右端点最大的线段,把这个线段加入到已覆盖区间里,并更新已覆盖的区间[L,R]值;(3)重复步骤(2)知道区间全部覆盖
3.最优装载问题
详见例题迷障
贪心的思路就是尽量找浓度小的药水。先对药水按浓度从小到大排序,药水的浓度不大于w%就加入,如果药水的浓度大于w%,计算混合后的浓度,不大于w%就加入,否则结束判断
4.多机调度问题
设有n个独立的作业,由m台相同的计算机进行工作。作业i的处理时间为ti,每个作业可在任何一台计算机上加工处理,但不能间断、拆分。要求给出一种作业调度的方式,在尽可能短的时间内,由m台计算机处理完成这n个作业
思路:(1)如果n<=m,需要的时间就是n个作业中处理时间最长的时间t
(2)如果n>m,首先将n个作业按处理时间从小到大排序,然后按照顺序把作业分配给空闲的计算机
三.
贪心还有很多用法:哈夫曼编码也是用贪心思想的
提供一个例题供大家参考poj-1521
string s;
priority_queue<int, vector<int>, greater<int>>Q;
while (getline(cin, s) && s != "END") {
int t = 1;
sort(s.begin(), s.end());
for (int i = 1; i < s.length(); i++) {
if (s[i] != s[i - 1]) {
Q.push(t);
t = 1;
}
else t++;
}
Q.push(t);
int ans = 0;
while (Q.size() > 1) {
int a = Q.top(); Q.pop();
int b = Q.top(); Q.pop();
Q.push(a + b);
ans += a + b;
}
Q.pop();
}
//ans就是编码后的长度