初识贪心
一、简单贪心
贪心法是求解一类最优问题的方法,它总是考虑当前状态下局部最优(或较优)的策略,来使全局的结果达到最优或者较优。
如果在想到某个似乎可行的策略,并且自己无法举出反例,那么就勇敢的去实现它。
输入格式
每个输入包含1个测试用例。每个测试用例先给出个不超过 1000 的正整数N表示月
饼的种类数以及不超过500 (以万吨为单位)的正整数D表示市场最大需求量,随后一行给
出N个正数表示每种月饼的库存量(以万成为单位),最后行给出N个正数表示每种月饼
的总售价(以亿元为单位)。数字间以空格分隔。
输出格式
对每组测试用例,在行中输出最大收益,以亿元为单位并精确到小数点后两位。
输入样例
3 20
18 15 10
75 72 45
输出样例
94.50
题意
现有月饼需求量为D,已知n种月饼各自的库存量和总售价,问如何销售这些月饼,使
得可以获得的收益最大。求最大收益。
思路
步骤1:这里采用“总是选择单价最高的月饼出售,可以获得最大的利润”的策略。
因此,对每种月饼,都根据其库存量和总售价来计算出该种月饼的单价。
之后,将所有月饼按单价从高到低排序。
步骤2:从单价高的月饼开始枚举。
①如果该种月饼的库存量不足以填补所有需求量,则将该种月饼全部卖出,此时需求量
减少该种月饼的库存量大小,收益值增加该种月饼的总售价大小。
②如果该种月饼的库存量足够供应需求量,则只提供需求量大小的月饼,此时收益值增
加当前需求量乘以该种月饼的单价,而需求量减为0。
这样,最后得到的收益值即为所求的最大收益值。
策略正确性的证明:假设有两种单价不同的月饼,其单价分别为a和b (a<b)。如果当
前需求量为K,那么两种月饼的总收入分别为aK与bK,而aK < bK显然成立,因此需要出
售单价更高的月饼。
注意点
①月饼库存量和总售价可以是浮点数(题目中只说是正数,没说是正整数),需要用
double型存储。对于,总需求量D虽然题目说是正整数,但是为了后面计算方便,也需要定
义为浮点型。很多得到“答案错误”的代码都错在这里。
2当月饼库存量高于需求量时,不能先令需求量为0,然后再计算收益,这会导致该步收益为0,
3.当月饼库存量高于需求量时,要记得将循环中断,否则出错。
参考代码:
1 #include<iostream> 2 #include<algorithm> 3 using namespace std; 4 5 struct mooncake{ 6 double store; //库存量 7 double sell; //总售价 8 double price; //单价 9 }cake[1010]; 10 11 bool cmp(mooncake a,mooncake b) 12 { 13 //按单价从高到低排序 14 return a.price>b.price; 15 } 16 17 int main() 18 { 19 int n,D; //月饼种类数和市场需求量 20 cin >> n >> D; 21 for(int i=0;i<n;i++) //输入每种月饼的库存量 22 { 23 cin >> cake[i].store; 24 } 25 for(int j=0;j<n;j++) //输入每种月饼的总售价并计算单价 26 { 27 cin >> cake[j].sell; 28 cake[j].price=cake[j].sell/cake[j].store; 29 } 30 sort(cake,cake+n,cmp); //单价从高到低排序 31 double w = 0; //收益 32 for(int i=0;i<n;i++) 33 { 34 if(cake[i].store <= D) //如果需求量高于库存量 35 { 36 D -= cake[i].store; //第i种月饼全部卖出 37 w += cake[i].sell; 38 } 39 else //库存量高于需求量 40 { 41 w += cake[i].price * D; //卖出剩余需求量的月饼 42 break; 43 } 44 } 45 printf("%.2f\n",w); 46 return 0; 47 }
[PAT B1023]组个最小数
题目描述
给定数字0-9各若干个。可以任意顺序排列这些数字,但必须全部使用。目标是使得最
后得到的数尽可能小(注意: 0不能做首位)。例如,给定两个0、 两个1、三个5和一个8,
得到的最小的数就是10015558.
现给定数字,请编写程序输出能够组成的最小的数。
输入格式
每个输入包含1个测试用例。每个测试用例在一行中给出 十个非负整数,顺序表示所相
有数字0、数字....数字9的个数。整数间用一个空格分隔。 十个数字的总个数不超过50.
且至少拥有一个非0的数字。
输出格式
在一行中输出能够组成的最小的数。
输入样例
2200030010
输出样例
10015558
思路
策略是:先从1~9中选择个数不为0的最小的数输出,然后从0~ 9输出数字,每个数
字输出次数为其剩余个数。
以样例为例,最高位为个数不为0的最小的数1,此后1的剩余个数减1 (由2变为1)。
接着按剩余次数(0剩余两个,1剩余一个,5出现三个,8出现一个)依次输出所有数。
策略正确性的证明:首先,由于所有数字都必须参与组合,因此最后结果的位数是确定
的。然后,由于最高位不能为0,因此需要从[1,9]中选择最小的数输出(如果存在两个长度
相同的数的最高位不同,那么定是最高位小的数更小)。 最后,针对除最高 位外的所有位,
也是从高位到低位优先选择[0, 9]中还存在的最小的数输出。
注意点
由于第一位不能是0, 因此第一个数字必须从 1 ~ 9中选择最小的存在的数字,且找到这
样的数字之后要及时中断循环。
参考代码
1 #include<iostream> 2 using namespace std; 3 int main() 4 { 5 int count[10]; //记录十个数字 6 for(int i=0;i<10;i++) 7 { 8 cin >> count[i]; 9 } 10 for(int i=1;i<10;i++) //从1~9种选择count不为0的最小数字 11 { 12 if(count[i] > 0) 13 { 14 cout << i; 15 count[i]--; 16 break; //找到一个后就中断 17 } 18 } 19 for(int i=0;i<10;i++) 20 { 21 for(int j = 0;j < count[i]; j++) 22 cout << i; 23 } 24 cout << endl; 25 return 0; 26 }
二、区间贪心
区间不相交问题
给出N个开区间(x,y),从中选择尽可能多的开区间,使得这些开区间两两没有交集;
1 #include<iostream> 2 #include<algorithm> 3 using namespace std; 4 const int maxn = 110; 5 struct Interval{ 6 int x,y; //开区间左右端点 7 }I[maxn]; 8 bool cmp(Interval a,Interval b) 9 { 10 if(a.x != b.x) 11 return a.x > b.x; 12 else 13 return a.y < b.y; 14 } 15 16 int main() 17 { 18 int n; 19 while(cin >> n) 20 { 21 for(int i = 0;i < n; i++) 22 cin >> I[i].x >> I[i].y; 23 sort(I,I+n,cmp); //把区间排序 24 //ans记录不想交区间个数,lastX记录上一个被选中区间的左端点 25 int ans = 1; 26 int lastX = I[0].x; 27 for(int i = 1;i < n;i++) 28 { 29 if(I[i].y <= lastX) 30 { 31 lastX = I[i].x; 32 ans ++; 33 } 34 } 35 cout << ans << endl; 36 } 37 return 0; 38 }