文章目录
更细致的分类下,三类常见の贪心题
最高性价比
相关知识
我们将性价比定义为单位投入量的获得量/单位获得量的投入量。也就是说,高性价比意味着
①同等投入量的高获得量 / ②同等获得量的低投入量
一般这类题目会给我们很多种选择,我们根据性价比将选择进行优先级高低排序,先选择高性价比的,再选择次性价比的,直到最低性价比的,这是我认为最简单的一类贪心,往往只涉及一个排序。
相关题目
老鼠和猫的交易——同等投入量的高获得量
参考思路
本质上就是问,小老鼠怎么利用同等投入量的(猫粮)获得最高获得量(五香豆),那么就需要引入一个性价比的概念,很显然,此题中的性价比可以只用用单位猫粮可获得的五香豆来定义。那么大体思路就出来了,根据每一个房间的性价比,进行性价比从高到低的排序,然后依据高性价比优先策略进行交换。
参考AC代码
#include<bits/stdc++.h>
using namespace std;
struct gat{
float amount,price,economy;
}gate[1005];
bool cmp(gat a,gat b){
return a.economy>b.economy;
} // 定义sort函数的性价比降序排列条件
int main(){
int num;
double money,gate_num,max_sum; // define money and gate number
ios::sync_with_stdio(false);
while(cin>>money>>gate_num){ // enter money and gate number
if(money==-1&&gate_num==-1)break;
for(int i=1;i<=gate_num;i++){ // iterate all the gates
cin>>gate[i].amount>>gate[i].price; // input the ith gate food amount and price
gate[i].economy=gate[i].amount/gate[i].price; // calculate the ith food economy
}
num=(int)gate_num; // 将门的个数的浮点数强制转换为整数形式保存在num里
sort(gate+1,gate+num+1,cmp); // sort函数依据gate进行性价比降序
max_sum=0; // 初始化最大购买量
for(int i=1;i<=gate_num;i++){ // 根据高性价比优先策略进行购买顺序的调整,即先买性价比高的,再买性价比低的
if(money-gate[i].price>=0){ //如果钱够,就全买;
max_sum+=gate[i].amount; //更新购买量
money-=gate[i].price; //更新所剩钱数
}
else{ //如果钱不够,就能买多少买多少
max_sum+=gate[i].amount*(money/gate[i].price); //更新购买量
money=0; //更新所剩钱数
}
}
printf("%.3f\n",max_sum); //保留3位小数
}
return 0;
}
田忌赛马——同等获得量的低投入量
参考思路
熟悉田忌赛马的同学都知道,国王的出马策略是先出最快的马,再除次快的马,以此类推,最后出最慢的马这样一个以马速降序的顺序来出马,但是我们的出马顺序则是可以随机拟定的。我们的目标是获取最多的胜场,那么胜场就是我们追求的获得量,而消耗的马的类型就是我们的投入量。战胜对方同一匹马,其实有很多种不同的出马策略,我们本着同等获得量时低投入量的原则,对于对方的同一匹马,我们尽可能派遣己方马速比对方马速高最少的那一匹,这样就可能保证增加同等的胜场,但是消耗最少的马力。不断地按照这个策略出马,就可以求出最大胜场。
想要达到这个思路,由于对方一定按降序出马,那么我们将己方的马也按照降序排列,然后按照顺序,依次选出第一个可以战胜对方的马,即可实现如上所述的“马尽其用”。
参考AC代码
#include<bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false);
int x,y,w,n;
int tian[1000],wang[1000];
while(cin>>n){
if(n==0)break;
w=0,x=0,y=0;
for(int i=0;i<n;i++){
cin>>tian[i];
}
for(int i=0;i<n;i++){
cin>>wang[i];
}
sort(tian,tian+n,greater<int>()),sort(wang,wang+n,greater<int>());
while(x<n&&y<n){
if(tian[x]>wang[y])w++,x++,y++;
else y++;
}
cout<<(2*w-n)*200<<endl;
}
return 0;
}
最高重叠度
相关知识
这类题目的题境,一般可以转译为,几个点在一条线上移动,移动过程中,点与点不能有交集,问我们最少移动多少时间/次。例如,在寝室楼道搬桌子,规定一段楼道只能搬运一张桌子,然后桌子分布在不同的寝室,问怎样合理分配,实现最少次数的搬运。
这类题中,我们将每一个个体的移动全程看成一个线段,为了更加地直观,我们可以在不同高度层画,然后我们就可以画出非常多条线段,如下所示:
最少移动多少次,本质上就是问,如果有一条竖线在向右移动,最多可以与多少条横线相交,如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I8q0BPxw-1646916048465)
为什么可以这样理解呢?这条竖线与横线的交点数,其实反映的就是最多不能同时做的事情数目。换言之,就是最少要分别做的事情数目。
怎么找最多交点数呢?显然,凭我们的直觉,当移动到如下所示的地方时,相交数最多,交点数为4:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3MKJ4vcE-1646916048466)(C:\Users\49593\AppData\Roaming\Typora\typora-user-images\image-20220301230707895.png)]
但是,在编程中怎么求解呢?我们可以利用循环遍历每一个线段所占据的位置,线段占据了的位置就+1,因此,对于一个横坐标x来说,有多少线段在这个坐标上出现过,它的数字就是多少。深入理解,请看以下题目。
相关题目
搬寝室
参考思路
首先处理编号问题第12、34、56,其实共享的是同样一个地点,所以,我们要让它们相等化,那就是将每一个端点加1再整除2即可。
其次,考虑一个方向问题,如果反向搬运,其实本质上和正向是一样的,那么干脆就把所有的反向改为正向的即可。
最后,如何求最高重叠度?那么就在每一个经过的位置点标记加1(因为是离散点),最多标记点的标记数,就是最高重叠度,最高重叠度再乘10,那么就是搬运任务需要的最少的时间。
参考AC代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
int i,j,T,N,s,t;
int r[200];
scanf("%d",&T);
while(T--)
{
scanf("%d",&N);
for(i=0;i<200;i++)
r[i]=0;
for(i=0;i<N;i++)
{
scanf("%d%d",&s,&t);
if(s>t){
j=s;s=t;t=j;
}
for(j=(s-1)/2;j<=(t-1)/2;j++)
r[j]++;
}
t=r[0];
for(i=1;i<200;i++)
if(t<r[i]) t=r[i];
printf("%d\n",t*10);
}
return 0;
}
最高活动数
相关知识
这类题目会告诉我们,有很多活动,每一项活动都占据一定的时间段,然后要求你求出最高可安排的活动数。
这种题型的解法就是按活动的结束时间进行升序排序,选最早完成的,然后在与前者不冲突的前提下选次早完成的,直到没有满足的为止,选出来的总数,就是要求的最高活动数。(可以用反证法证明,直接当结论记即可)
相关题目
今年暑假不AC
参考思路
由于有多个节目,因此我们开一个节目的数组,由于每个节目有开始时间和结束时间两个属性,因此我们利用结构体存储节目信息,因此我们用结构体数组存储这些节目的信息。按照结束时间进行升序排序,选最早完成的,然后在与前者不冲突的前提选次早完成的,知道没有满足的位置,选出来的总数,就是要求的最高活动数。
需要留心的是,由于是多组测试数据,所以在每一轮汇总,都一定要进行数据初始化。
参考AC代码
#include<bits/stdc++.h>
using namespace std;
struct jiemu{ //定义结构体来存储节目的起始时间两个数据
int a;
int b;
};
bool cmp(jiemu x, jiemu y){ //排序时先按结束时间从小到大排序,如结束时间相同,按开始时间从大道小排序
if(x.b==y.b) return x.a>y.a;
return x.b<y.b;
}
int main()
{
int x,y,s,N;
jiemu jm[100];
ios::sync_with_stdio(false);
while(cin>>N){
if(N==0) break;
x=0,y=1,s=1; //循环的话,每次的初始化很重要,初始化的内容,就是与结果相关的一切内容
for(int i=0;i<N;i++){
cin>>jm[i].a>>jm[i].b;
}
sort(jm,jm+N,cmp);
while(x<N&&y<N){ //这里的逻辑一定是要与,不然会出错
if(jm[x].b<=jm[y].a) s++,x=y,y++; //找到合适的,长度加一,更新被最新的节目x为刚才的y,再将y右移
else y++; //没找到,y还是右移
}
cout<<s<<endl;
}
return 0;
}