贪婪者总是一贫如洗 ——克劳德兰纳斯
目录
什么是贪心
怎样实现
例一
AC代码
例二
AC代码
总结
步骤
写在最后
360百科里是这样说的:贪婪算法是一种对某些求最优解问题的更简单、更迅速的设计技术。用贪婪法设计算法的特点是一步一步地进行,常以当前情况为基础根据某个优化测度作最优选择,而不考虑各种可能的整体情况,它省去了为找最优解要穷尽所有可能而必须耗费的大量时间,它采用自顶向下,以迭代的方法做出相继的贪心选择,每做一次贪心选择就将所求问题简化为一个规模更小的子问题,通过每一步贪心选择,可得到问题的一个最优解,虽然每一步上都要保证能获得局部最优解,但由此产生的全局解有时不一定是最优的,所以贪婪法不要回溯。
贪婪算法是一种改进了的分级处理方法。其核心是根据题意选取一种量度标准。然后将这多个输入排成这种量度标准所要求的顺序,按这种顺序一次输入一个量。如果这个输入和当前已构成在这种量度意义下的部分最佳解加在一起不能产生一个可行解,则不把此输入加到这部分解中。这种能够得到某种量度意义下最优解的分级处理方法称为贪婪算法。
通俗的解释一下:贪心,顾名思义,就是找出某个人最想要的,或者怎样最大限度的接近,比如怎样才能用一定的钱买到最多的苹果等等。
这里,只介绍仅有贪心算法的实现,不含其他算法,结合例题更易懂:
P1478 陶陶摘苹果(升级版)
题目描述
又是一年秋季时,陶陶家的苹果树结了 n 个果子。陶陶又跑去摘苹果,这次他有一个 a 公分的椅子。当他手够不着时,他会站到椅子上再试试。
这次与 NOIp2005 普及组第一题不同的是:陶陶之前搬凳子,力气只剩下 s 了。当然,每次摘苹果时都要用一定的力气。陶陶想知道在s<0之前最多能摘到多少个苹果。
现在已知 n 个苹果到达地上的高度 x,椅子的高度 a,陶陶手伸直的最大长度 b,陶陶所剩的力气 s,陶陶摘一个苹果需要的力气 y,求陶陶最多能摘到多少个苹果。
输入格式
第 1 行:两个数 苹果数 n,力气 s。
第 2 行:两个数 椅子的高度 a,陶陶手伸直的最大长度 b。
第 3 行~第 3+n-1行:每行两个数 苹果高度 x,摘这个苹果需要的力气 y。
输出格式
只有一个整数,表示陶陶最多能摘到的苹果数。
首先,这道题应该把陶陶摘不到的苹果排除掉(即xi>b+a);之后,就用到贪心,怎样才能用有限的力气得到最多的苹果?所以,应用sort排一下序,再判断力气大于0时最多能摘到的苹果(即何时力气<0);
#include
using namespace std;
int main()
{
int n,s,a,b,x[5001],y[5001],ans=0;
cin>>n>>s>>a>>b;
for(int i=0;i<=n-1;i++)
{
cin>>x[i]>>y[i];
if(x[i]>a+b) x[i]=-1,y[i]=-1;//判断能否摘到
}
sort(y,y+n);//排序
for(int i=0;i<=n-1;i++)//判断最多摘到的
{
if(y[i]>=0)
{
s-=y[i];
ans++;
}
if(s-y[i+1]<0) break;//力气小于0,跳出循环
}
cout<
举个例子:假设一共8个苹果,椅子20,身高130,力气15
高度 | 所需力气 | 能否够到 | 力气分配 |
120 | 3 | 能 | 3 |
150 | 2 | 能 | 2 |
110 | 7 | 能 | |
180 | 1 | 否 | |
50 | 8 | 能 | |
200 | 0 | 否 | |
140 | 3 | 能 | 3 |
120 | 2 | 能 | 2 |
所以答案为4.
Mila 的木棍
Mila 找到了 n 根木棍,她将这 nn 根木棍排成一行形成一个序列 a,但是她发现此时的序列可能是不优美的。
假如木棍的长度从左到右形成一个不下降序列,那么 Mila 认为这些木棍才是优美的。因此,Mila 学习了一种可以削减木棍长度的魔法:对一个区间 [l,r] 使用一次魔法,可以将这个区间内所有木棍的长度减 1。
不下降序列:对于所有 1≤j
现在 Mila 想要知道:至少需要使用多少次魔法才可以将这些木棍变得优美?
输入格式
第一行一个整数 n,表示木棍的数量。
第二行 n 个以空格隔开的整数,表示这个序列,第 i 根木棍的长度为 ai。
输出格式
输出共一行一个整数,表示答案。
当初一看题,以为是要看题面写dp,结果仔细研究样例后,发现这是简单的贪心。为什么呢?因为研究样例后,就会发现如果a[i]>a[i+1],a[i]就应该被施魔法,是典型的贪心算法;区间有什么用呢,貌似没用,好像为了样例更容易解释吧(代码有更详细解释)
#include
using namespace std;
long long int b[1000001];
int main()
{
//freopen("stick.in","r",stdin);
//freopen("stick.out","w",stdout);
long long int a,maxx=0,ans=0;
cin>>a;
for(int i=0;i<=a-1;i++)
{
cin>>b[i];
}
int j=a-1;//注意要倒序循环
while(j!=-1)
{
if(b[j]b[j-1])
{
j--;//跳过
}
}
j--;
}
cout<
1.拿到这道题,首先确定算法,不然会一头雾水(所有题都是这样,下边步骤是针对贪心);
2.找到规律:根据题面要求。然后通过模拟实现(当然,有很多题是涉及多种算法的,这就得根据不同要求适时调整),之后就AC了。
在这里,我简单的介绍一下贪心的用途(因为我了解不多):
贪心算法和动态规划算法关系密切。在动态规划算法过程中,需要根据递推关系不断进行选择,在选择时需要根据最终问题的解来决定最佳选择,但是对于某些问题来说,可以采取一种更加简洁的选择策略——只选择当前最佳的,即每次通过局部的最优解来构造一个全局的最优解。
区间调度是一个可以使用贪心算法解决的典型问题。区间调度是指给定 n 个区间[ai,bi],1≤i≤n,ai<bi,要求确定区间的一个子集,该子集所包含的区间互不重叠且区间的数量最大。
——摘自《C++,挑战编程程序设计竞赛进阶训练指南》
感谢大家阅读~~~