贪心法----选取当前最优策略
贪心的特点是选取当前最优策略,这也带来了缺点,当前的最优解不一定是最终的最优解。
1.硬币问题
有1元、5元、10元、50元、100元、500元的硬币各C1、C5、C10、C50、C100、C500枚。现在要用这些硬币来支付A元,最少需要多少枚硬币?假定本题至少存在一种支付方案。
样例
输入
3 2 1 3 0 2 620
(C1、C5、........C500、A)
输出
6
(500+50*2+10+5*2)
提示:优先使用面值大的硬币
#include<iostream>
#include<algorithm>
using namespace std;
const int V[6]={1,5,10,50,100,500};
int C[6];
int A;
void solve(){
int ans=0;
for(int i=5;i>=0;i--){
int t=min(A/V[i],C[i]);
A-=t*V[i];
ans+=t;
}
cout<<ans<<endl;
}
int main(){
for(int i=0;i<6;i++) cin>>C[i];
cin>>A;
solve();
return 0;
}
2.区间调度问题
有n项工作,每项工作分别在si开始,ti结束。对每项工作,你都可以选择参加或不参加,但选择了参加某项工作就必须至始至终参加全程参与,即参与工作的时间段不能有重叠(即使开始的时间和结束的时间重叠都不行)。
样例:
输入
n=5
s={1,2,4,6,8}
T={3,5,7,9,10}
输出
3(选择工作1, 3, 5)
解题分析:
对这个问题,如果使用贪心算法的话,可能有以下几种考虑:
(1)、每次选取开始时间最早的;
(#)、每次选取结束时间最早的;
(2)、每次选取用时最短的;
(3)、在可选工作中,每次选取与最小可选工作有重叠的部分;
只有算法(#)是正确的。
对于(1):
对于(2):
对于(3):
具体证明如下:
数轴上有n个区间,选出最多的区间,使得这些区间不互相重叠。
算法:
将所有区间按右端点坐标从小到大排序,顺序处理每个区间。如果它与当前已选的所有区间都没有重叠,则选择该区间,否则不选。
证明:
显然,该算法最后选出的区间不互相重叠,下面证明所选出区间的数量是最多的。设fi为该算法所接受的第i个区间的右端点坐标,gi为某最优解中的第i个区间的右端点坐标。
命题1.1 当i>=1时,该算法所接受的第i个区间的右端点坐标fi<=某最优解中的第i个区间的右端点坐标gi。
该命题可以运用数学归纳法来证明。对于i=1,命题显然为真,因为算法第一个选择的区间拥有最小右端点坐标。令i>1,假定论断对i-1为真,即fi-1<=gi-1。则最优解的第i个可选区间所组成的集合包含于执行该算法时第i个可选区间所组成的集合;而当算法选择第i个区间时,选的是在可选区间中右端点坐标最小的一个,所以有fi<=gi。证毕。
设该算法选出了k个区间,而最优解选出了m个区间。
命题1.2 最优解选出的区间数量m=该算法选出的区间数量k。
假设m>k,根据命题1.1,有fk<=gk。由于m>k,必然存在某区间,在gk之后开始,故也在fk之后开始。而该算法一定不会在选了第k个区间后停止,还会选择更多的区间,产生矛盾。所以m<=k,又因为m是最优解选出区间个数,所以m=k。
综上所述,算法选出的区间是最优解。