贪心算法:贪心选择是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。每做一次贪心选择就将所求问题简化为一个规模更小的子问题。对于一个具体问题,要确定它是否具有贪心选择的性质,我们必须证明每一步所作的贪心选择最终能得到问题的最优解。通常可以首先证明问题的一个整体最优解,是从贪心选择开始的,而且作了贪心选择后,原问题简化为一个规模更小的类似子问题。然后,用数学归纳法证明,通过每一步贪心选择,最终可得到问题的一个整体最优解。
例题1:硬币问题
有1元、5元、10元、50元、100元、500元的硬币各C1、C2、C3、C4、C5、C6枚,现在要用这些硬币来支付A元,问:最少需要多少枚硬币?本题目假设至少存在一种解决方案。
样例输入:(前6项为各种面值的硬币数量、最后一个数字为需要支付的钱)
3 2 1 3 0 2 620
样例输出:
6
思想:首先使用面值最大的硬币进行贪心算法。直到解决问题.
代码表示:
#include<iostream>
using namespace std;
int main()
{
const int coin[6]={1,5,10,50,100,500};
int C[6];
int A;
for(int i=0;i<6;i++)
cin >> C[i];
cin >> A; //输入
int ans=0;
for(int i=5;i>=0;i--)
{
int t=min(A/coin[i],C[i]); //选择A/coin[i],C[i]较小的值 硬币数
A=A-t*coin[i]; //钱减去硬币数乘上当前面值
ans+=t; //硬币总数累加
}
cout << ans << endl;
return 0;
}
例题2:
区间问题
有n项工作,每项工作分别在时间开始、在时间结束。对于每项工作你都可以选择参加与否。如果选择了参与,那么自始至终都必须全程参与。此外,参与工作的时间段不能重叠(即参与该项目的时间内不能参加别的项目)。如图所示:
你的目标是参与尽可能多的工作,那么最多能参与多少项工作呢?
限制条件:1<= N <= 100000 1 <= <= <=1e9
样例输入:(输入顺序为n(n个时间段)、n个开始时间、n个结束时间)
5
1 2 4 6 8
3 5 7 9 10
样例输出:
3
这个问题也可以用贪心算法来解决,但不像硬币问题那样简单。我们可以设计出各种各样的贪心算法,例如:
1.在可选工作中,每次都选取开始时间最早的工作;
2.在可选工作中,每次都选取结束时间最早的工作;
3.在可选工作中,每次都选择用时最短的工作。
这些策略都是很容易想的到的、但是并不是全部策略都是能解决问题的。
我们很容易可以得出算法1的反例:
不能处理的例子
算法3的反例:
算法3不能处理的例子
只有算法2是正确的、而其余两种都可以找到对应的反例。(每次都选择结束时间最早的时间段)
代码表示:
#include<iostream>
#include<algorithm>
#define MAXN 100000+2
using namespace std;
struct T{
int s,t;
};
bool cmp(T a,T b)
{
if(a.t!=b.t) return a.t<b.t; //如果a.t != b.t 小的在前
else return a.s<b.s;
}
int main()
{
struct T time[MAXN];
int n;
cin >> n;
for(int i=0;i<n;i++)
cin >> time[i].s;
for(int i=0;i<n;i++)
cin >> time[i].t;
//排序函数
sort(time,time+n,cmp);
// ans表示工作数量、t表示最后所选工作的结束时间
int ans=0,t=0;
for(int i=0;i<n;i++)
{
if(t<time[i].s) //如果当前工作结束时间小于下一个工作开始时间
{
t=time[i].t; //更新时间t
ans++; //次数++
}
}
cout << ans << endl;
return 0;
}