一、概念(是什么)
贪心算法(Greedy Algorithm)是一种在解决问题时,每一步都采取在当前看来是最好的选择,希望通过局部最优的选择,达到全局最优的算法策略。
二、适合的题型(为什么)
贪心算法适合解决那些具有最优子结构和贪心选择性质的问题。最优子结构意味着问题的最优解包含其子问题的最优解,而贪心选择性质意味着在每一步选择中,都能做出在当前看来最优的选择。以下是一些适合使用贪心算法解决的问题:
1.最小生成树:例如,克鲁斯卡尔算法(Kruskal’s algorithm)和普里姆算法(Prim’s algorithm)都是使用贪心策略来构建最小生成树的。
哈夫曼编码:哈夫曼算法通过贪心选择频率最小的字符来构建哈夫曼树,从而实现字符的最优编码。
2.活动选择问题:在给定的活动中选择最多数的活动,使得这些活动的总时间不超过一天的时间限制。
3.背包问题:特别是在0-1背包问题中,贪心算法可以用来寻找一个近似最优解。
最长公共子序列(LCS):虽然LCS问题本身不适合使用贪心算法,但是贪心算法可以用来找到LCS的一个最长子序列。
4.股票买卖问题:在给定的n天中,找到最大利润的买卖组合。
最小路径和:在加权图中找到从源点到目的地的路径,使得路径上权值之和最小。
元素覆盖问题:例如,最小数组覆盖问题,要求选择最少的数组元素,使得这些元素的和覆盖所有其他元素的和。
贪心算法在处理这些问题时,通常能够提供多项式时间复杂度的解决方案,这在很多情况下是非常有吸引力的。然而,贪心算法不总是能得到全局最优解,因此在使用贪心算法之前,需要对问题进行适当的分析,确保它满足最优子结构和贪心选择性质。如果不满足这些条件,可能需要考虑其他算法策略
三、解题步骤(怎么做)
使用贪心算法解题通常需要以下几个步骤:
1.理解问题:首先要充分理解问题的性质,确定问题的目标是什么,以及如何衡量解的好坏。
2.贪心选择:根据问题的性质,找到一个局部最优解的选取策略,这个策略应该能够使得问题的子结构最优。也就是说,在这个步骤中,你需要确定在每一步决策中,如何做出选择以达到局部最优。
3.问题的分解:将原问题分解为若干个子问题,每个子问题都是原问题的一个局部情况。
贪心算法实现:根据贪心选择策略,对每个子问题采用局部最优解。
4.子问题的合并:将每个子问题的解合并起来,得到原问题的一个解。
5.结果验证:最后需要验证通过贪心算法得到的解是否是全局最优解。由于贪心算法只考虑局部最优解,所以并不保证得到的是全局最优解,这一步是非常重要的。
四、例题
一
题目描述
xx老师是业内大牛,许多学生都想跟xx老师学习,其中也包括dd。老师知道dd在学编程,就对dd说,如果你能做出下面这题,并且你能很好的学会动态规划、搜索、图论等内容,就收dd为徒。
为了能投到老师门下,DD开始认真思考这问题:
给定一个正整数N,求最小的、比N大的正整数M,使得M与N的二进制表示中有相同数目的1。
举个例子,假如给定的N为78,其二进制表示为1001110,包含4个1,那么最小的比N大的并且二进制表示中只包含4个1的数是83,其二进制是1010011,因此83就是答案。
输入格式
输入若干行,每行一个数n(1≤n≤1000000),输入"0"结束。
输出格式
输出若干行对应的值。
输入样例
1 2 3 4 78 0
输出样例
2 4 5 8 83
#include <iostream>
#include <bitset>
int countOnes(int n) {
int count = 0;
while (n) {
count += n & 1;
n >>= 1;
}
return count;
}
int findNextNumber(int n) {
int ones = countOnes(n);
do {
n++;
} while (countOnes(n) != ones);
return n;
}
int main() {
int n;
while (std::cin >> n && n != 0) {
std::cout << findNextNumber(n) << std::endl;
}
return 0;
}
二
题目描述
Farmer John最近为奶牛们的图书馆添置了一个巨大的书架,尽管它是如此 的大,但它还是几乎瞬间就被各种各样的书塞满了。现在,只有书架的顶上还留 有一点空间。
所有头奶牛都有一个确定的身高。设所有奶牛身高的和为S,书架的高度为B。
为了够到比最高的那头奶牛还要高的书架顶,奶牛们不得不象演杂技一般, 一头站在另一头的背上,叠成一座“奶牛塔”。当然,这个塔的高度,就是塔中 所有奶牛的身高之和。
为了往书架顶上放东西,所有奶牛的身高和必须不小于书 架的高度。显然,塔中的奶牛数目越多,整座塔就越不稳定,于是奶牛们希望在 能够到书架顶的前提下,让塔中奶牛的数目尽量少。
现在,奶牛们找到了你,希望你帮她们计算这个最小的数目。
输入格式
* 第1行: 2个用空格隔开的整数:N 和 B * 第2..N+1行: 第i+1行是1个整数:H_i
N(1 < = N < = 20,000)
H_i (1 < = H_i < = 10,000)
并且保证 1 < = B < = S < 2,000,000,007
输出格式
* 第1行: 输出1个整数,即最少要多少头奶牛叠成塔,才能够到书架顶部
输入样例
解释
6 40 6 18 11 13 19 11
输出样例
3
数据范围与提示
输入说明: 一共有6头奶牛,书架的高度为40,奶牛们的身高在6..19之间。 输出说明: 一种只用3头奶牛就达到高度40的方法:18+11+13。当然还有其他方法,在 此不一一列出了。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int N, B;
cin >> N >> B;
vector<int> heights(N);
for (int i = 0; i < N; i++) {
cin >> heights[i];
}
sort(heights.begin(), heights.end(), greater<int>());
int sum = 0;
int count = 0;
for (int i = 0; i < N; i++) {
sum += heights[i];
count++;
if (sum >= B) {
break;
}
}
cout << count << endl;
return 0;
}
三
题目描述
假设山洞里有n种宝物,每种宝物有一定重量w和相应的价值v,毛驴运载能力有限,只能运走m重量的宝的宝物,一种宝物只能拿一样,宝物可以分割。那么怎么才能使毛驴运走宝物的价值最大呢?
#include<bits/stdc++.h>
using namespace std;
struct bao_wu{
double a;
double b;
double c;
}s[10001];
bool cmp(bao_wu a,bao_wu b){
return a.c>b.c;
}
int main(){
int n;
double k;
double sum=0;
cin>>n>>k;
for(int i=0;i<n;i++){
cin>>s[i].a>>s[i].b;
s[i].c=s[i].b/s[i].a;
}
sort(s,s+n,cmp);
for(int i=0;i<n;i++){
if(s[i].a<k){
k=k-s[i].a;
sum+=s[i].b;
}
else{
sum+=k*s[i].c;
break;
}
}
printf("%.1lf",sum);
return 0;
}