写在前面
贪心算法是一个应用非常广泛的算法,由于我自己对贪心算法的认识也不够深刻,因此会比较偏重于从题目入手来理解和掌握贪心算法。
1.1 定义
对于贪心算法的规范性定义可以参考维基百科:贪心法,下面是一段来自维基百科的定义:
贪心法,又称贪心算法、贪婪算法、或称贪婪法,是一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是最好或最优的算法。
以上定义理解起来并不难,下面通过几道例题来对贪心算法展开分析。
1.2 例题分析
例题1(找零钱问题)
小x买了个钱包,结果买完就没钱放了,一气之下将钱包搁置箱底,常常忘记带出来。但是没有钱包的话,纸币放在口袋里很不方便,容易乱,也不容易掉,所以每次有买什么东西的时候,他都会让收银员找给他最少张数的纸币。收银员忙于找零,经常没法顾及这个问题,所以求助会编程的你。
正如我们所知,纸币面值一般有1元,5元,10元,20元,50元,100元。
输入格式:
输入包含多组数据。
输入第一行包含一个整数 N(N<10000) N ( N < 10000 ) ,表示要找的零钱总额。
输出格式:
每次输出一个整数表示最少张数的纸币
SampleInput:
75
13
SampleOutput:
3
4
分析:
找零钱问题是在我们生活中也非常常见的一个问题,例如我们拿了一张大面值的钞票(100块)去买东西,如果只是买一个2元的东西,需要找98元,那么一般售货员会先找50元面值的钞票,再找20元面值的钞票,再找10元、5元等面值的钞票使得找的钞票的张数最少。因此有了这样的思路,为了使得张数最少,只需要尽量用面额大的钞票即可。
具体算法实现如下:
需要找N的零钱,每次取出小于N且面额最大的零钱,纸币张数加1,然后N赋值为剩下要找的零钱,以此类推直到N = 0
完整实现代码如下:
#include <iostream>
using namespace std;
int main() {
std::ios::sync_with_stdio(false);
int money[6] = {100, 50, 20, 10, 5, 1};
int n;
while (cin >> n) {
int sum = 0;
for (int i = 0; i < 6;i++) {
if (n >= money[i]) {
sum += n / money[i];
n = n % money[i];
}
}
cout << sum << endl;
}
return 0;
}
关于找零钱还有一个值得注意的问题
假设纸币的面值只有三种:1、3、4。在同样的问题中,如果使用贪心策略,将是错误的。
具体:假设需要找6元的零钱,那么先拿出面额最大的一张四元,然后剩下2元只能用2张一元,于是使用了3张纸币。
然而实际上,我们只需要2张三元的纸币即可。
思考:在面额满足什么条件下才能使用本贪心策略?(这个问题涉及到后续的算法内容,等到有了相关知识基础再来解决这个问题。)
例题2(区间问题)(较难)
有n项工作,每项工作分别在 si s i 时间开始,在 ti t i 时间结束。对于每项工作,你都可以选择参加与否,如果选择了参与,那么自始至终都必须参加全程参与。此外,参与工作的时间段不能有重叠(即使是开始的时间和结束的时间重叠都不行)。
——出自《挑战程序设计竞赛》P40
你的目标是参与尽可能多的工作,那么最多能参与多少项工作呢?
例如:
限制条件:
- 1≤N≤10000 1 ≤ N ≤ 10000
- 1≤si≤ti≤109 1 ≤ s i ≤ t i ≤ 10 9
SampleInput:
5 (n的值)
1 2 4 6 8 (s的值)
3 5 7 9 10 (t的值)
(对应上图)
SampleOutput:
3 (选取工作1、3、5)
(尚未截稿)