贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,所得的是某种意义上的局部最优解。
贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态。一般来说,如果在想到某个似乎可行的策略,并且自己无法举出反例,那么就勇敢地实现它。
贪心算法的基本思路是从问题的某一个初始解出发一步一步地进行,根据某个优化测度,每一步都要确保能获得局部最优解。每一步只考虑一个数据,它的选取应该满足局部优化的条件。若下一个数据和部分最优解连在一起不再是可行解时,就不把该数据添加到部分解中,直到把所有数据枚举完,或者不能再添加算法停止
。
月饼是中国人在中秋佳节时吃的一种传统食品,不同地区有许多不同风味的月饼。现给定所有种类月饼的库存量、总售价以及市场的最大需求量,试计算可以获得的最大收益是多少。
注意:销售时允许取出一部分库存。样例给出的情形是这样的:假如有三种月饼,存量分别为18/15/10万吨,总售价分别为75、72、45亿元。如果市场的最大需求量只有20万吨,那么最大收益策略应该是卖出全部15万吨第二种月饼以及5万吨第三种月饼,得72+45/2=94.5(亿元)。
每个输入包含1个测试用例。每个测试用例先给出一个不超过1000的正整数N表示月饼的种类数以及不超过500(以万吨为单位)。数字间以空格分隔。
对每组测试用例,在一行中输出最大收益,以亿元为单位并精确到小数点后两位。
3 20
18 15 20
45 72 45
94.5
现有月饼需求量为D,已知n种月饼各自的库存量和总售价,问如何销售这些月饼,使得可以收获的利益最大。求最大收益。
假设有两种单价不同的月饼,其单价分别为a和b(a
注意事项
①月饼库存量和总售价可以是浮点数(题目中只说是正数,没说是正整数),需要用double型存储。对于,总需求量D虽然题目说是正整数,但是为了后面计算方便,也需要定义为浮点型。
②当月饼库存量高于需求量时,不能先令需求量为0,然后再计算收益,这会导致收益为0.
③当月饼库存量高于需求量时,要记得将循环中断,否则会出错。
代码
#include
#include
using namespace std;
struct mooncake{
double store;//库存量
double sell;//总售价
double price;//单价
}cake[1010];
//按单价从高到低排序
bool cmp(mooncake a,mooncake b){
return a.price>b.price;
}
int main(int argc, char** argv) {
int n;
double D;//总需求量
scanf("%d%lf",&n,&D);
for(int i=0;i<n;i++){
scanf("%lf",&cake[i].store);
}
for(int i=0;i<n;i++){
scanf("%lf",&cake[i].sell);
//计算 单价
cake[i].price=cake[i].sell/cake[i].store;
}
sort(cake,cake+n,cmp);
double ans=0;//收益
for(int i=0;i<n;i++){
//如果需求量高于月饼库存量
if(cake[i].store<=D){
//第i种月饼全部卖出
D-=cake[i].store;
ans+=cake[i].sell;
}else{
//如果月饼库存量高于需求量
ans+=cake[i].price*D;//只卖出剩余需求量的月饼
break;
}
}
printf("%.2f\n",ans);
return 0;
}
给定数字0~9各若干个。可以任意顺序排列这些数字,但必须全部使用。目标是使得最后得到的数尽可能小(注意:0不能做首位)。例如,给定两个0、两个1、三个5和一个8,得到的最小的数就是10015558.
现给定数字,请编写程序输出能够组成的最小的数。
每个输入包含1个测试用例。每个测试用例在一行中给出十个非负整数,顺序表示所有数字0、数字1…数字9的个数。整数间加一个空格分隔。十个数字的总个数不超过50,且至少拥有一个非0的数字。
在一行中输出能够组成的最小的数。
2 2 0 0 0 3 0 0 1 0
10015558
首先,由于所有数字都必须参与组合,因此最后结果的位数是确定的。然后,由于最高位不能为0,因此需要从[1,9]中选择最小的数输出(如果存在两个长度相同的数的最高位不同,那么一定是最高位小的数更小)。最后,针对除最高位外的所有位,也是从高位到低位优先选择[0,9]中还存在的最小的数输出。
由于第一位不能是0,因此第一个数字必须从1~ 9中选择最小的存在的数字,且找到这样的数字之后要及时中断循环。
#include
#include
using namespace std;
int main(){
int count[10];//记录数字0~9的个数
for(int i=0;i<10;i++){
scanf("%d",&count[i]);
}
for(int i=1;i<10;i++){
//从1~9中选择count不为0的最小的数字
if(count[i]>0){
printf("%d", i);
count[i]--;
break;//找到一个之后就中断
}
}
for(int i=0;i<10;i++){
//从0~9中输出对应个数的数字
for(int j=0;j<count[i];j++){
printf("%d",i);
}
}
return 0;
}
有一个背包,背包容量是M=150。有7个物品,物品可以分割成任意大小。要求尽可能让装入背包中的物品总价值最大,但不能超过总容量。
物品 | 重量 | 价值 |
---|---|---|
A | 35 | 10 |
B | 30 | 40 |
C | 60 | 30 |
D | 50 | 50 |
E | 40 | 35 |
F | 10 | 40 |
G | 25 | 30 |
物品 | 价值 | 重量 |
---|---|---|
B | 120 | 30 |
A | 60 | 20 |
物品 | 价值 | 重量 |
---|---|---|
C | 50 | 10 |
A | 60 | 20 |
物品 | 单位价值 | 重量 |
---|---|---|
C | 50/10=5 | 10 |
B | 12./30=4 | 30 |
算法设计
①计算出每个物品单位重量的价值
②按单位价值从大到小将物品排序
③根据背包当前所剩容量选择物品
④如果背包的容量大于当前物品的重量,那么就将当前物品装进去。否则,就将当前物品舍去,然后跳出循环结束。
参考代码
#include
#include
using namespace std;
typedef struct{
int w;
int v;
double avg;
}P;
bool cmp(P a,P b){
return a.avg>b.avg;
}
int main(){
P *p;
int n,i,m;//n 物品个数 m背包容量
while(cin>>n>>m){
p=new P[n];
for(i=0;i<n;i++){
cin>>p[i].w>>p[i].v;
p[i].avg=p[i].v/p[i].w*1.0;
}
sort(p,p+n,cmp);
int maxvalue=0;
for(i=0;i<n;i++){
if(p[i].w<=m){
m-=p[i].w;
maxvalue+=p[i].v;
}else{
break;
}
}
cout<<maxvalue<<endl;
}
return 0;
}
给出N个开区间(x,y),从中选择尽可能多的开区间,使得这些开区间两两没有交集。例如对开区间(1,3)、(2,4)、(3,5)、(6,7)来说,可以选出最多三个区间(1,3)、(3,5)、(6,7),它们互相没有交集。
#include
#include
using namespace std;
const int maxn=110;
struct Inteval{
int x,y;//开区间左右端点
}I[maxn];
bool cmp(Inteval a,Inteval b){
if(a.x!=b.x){
//先按左端点从大到小排序
return a.x>b.x;
}else{
//左端点相同的按右端点从小到大排序
return a.y<b.y;
}
}
int main(){
int n;
while(scanf("%d",&n),n!=0){
for(int i=0;i<n;i++){
scanf("%d%d",&I[i].x,&I[i].y);
}
sort(I,I+n,cmp);//把区间排序
//ans记录不相交区间个数,lastX记录上一个被选中区间的左端点
int ans=1,lastX=I[0].x;
for(int i=1;i<n;i++){
//如果该区间右端点在lastX左边
if(I[i].y<=lastX){
lastX=I[i].x;//以I[i]作为新选中的区间
ans++;//不相交区间个数加1
}
}
printf("%d\n",ans);
}
}
【1】五大常用算法之一:贪心算法
【2】五大基本算法之贪心算法 Greedy
【3】贪心算法—机器之心
【4】贪心算法总结
【5】算法笔记