贪心算法例题与讲解(1)
创始人
2024-05-30 23:54:30
0

文章首发于:My Blog 欢迎大佬们前来逛逛

P2240 部分背包问题

P2240 【深基12.例1】部分背包问题 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

有点像01背包,但是又不是,01背包的物品的是不能再分的,而本题的物品是可以再分的,因此我们可以根据某个物品的部分重量和单位价值算出它的部分最大价值来作为我们选择的物品。

  1. 首先计算单位价值 = 总价值 / 总重量

  1. 按照贪心的思想,则单位价值大的一定是越优的,因此排序,单位价值大的排在前面。

  1. 按照单位价值排好序的顺序一个一个物品选,若背包的剩余容量不够则取某个物品的部分最大价值,然后退出即可。

//TODO: Write code here
int n,T;
const int N=1e4+10;
struct Node
{double w,v,pro;
}node[N];
bool cmp(Node a,Node b)
{if (a.pro>b.pro){return true;}return false;
}
signed main()
{cin>>n>>T;for (int i=1;i<=n;i++){cin>>node[i].w>>node[i].v;node[i].pro=node[i].v/node[i].w;	//计算单位价值}sort(node+1,node+1+n,cmp);//按照单位价值排序double ans=0;double temp=T;for (int i=1;i<=n;i++){if (temp>=node[i].w){ans+=node[i].v;temp-=node[i].w;}else{ans+=(temp*node[i].pro);//取最大部分价值break;}}printf("%.2lf",ans);
#define one 1return 0;
}

P1223 排队接水

排队接水

排队接水:一列人排成一队,接水的人有个接水的时间,而其他人都要等这个人接完水,然后再轮流接水,已经接完水的就完成任务了,即不需要算在排队等接水的人当中了。

我们要计算n个人的平均接水时间,因此需要把当前人接水的时间 * 后面等待的总人数

因此我们可以总结出:最先接水的一定是接水所需时间最少的。为什么?

  • 如果你的接水时间是 1000 ,那么除这个人之外的9个人就需要等待 9 * 1000的时间。

  • 如果你的接水时间是 10 ,那么除这个人之外的9个人就需要等待 9 * 10 的时间。

很显然按照接水时间短的先接水的这种做法一定是最优的。

那么就直接按照时间排序即可,然后计算 平均的等待时间,最后再除以一次总人数。

//TODO: Write code here
int n,m;
const int N=1e6+10;
int nums[N],presum[N];
struct Node
{int num,val;
}node[N];
bool comp(Node a,Node b)
{if (a.val>n;for (int i=1;i<=n;i++){node[i].num=i;cin>>node[i].val;}sort(node+1,node+1+n,comp);// 按照接水的时间排序,如果时间相等,则按序号double ans=0;for (int i=1;i<=n;i++){ans+=(n-i)*node[i].val;//后面的每个人都需要等这个时间cout<

P1803 凌乱的yyy / 线段覆盖

P1803 凌乱的yyy / 线段覆盖

这道题其实就是贪心的经典问题:给出任务的开始和结束时间,求总共能完成的任务的最大数量

贪心思路:

  • 结束时间早的一定是最优的(在此不给出证明)

过程如下;

  1. 因此把每个任务按照结束时间排序,结束时间早的是最优的。

  1. 按照结束时间早正序排序,如果下一个任务的开始时间在上一个任务的进行过程中,则说明还没有完成上一个任务,因此跳过这个任务,只能是下一个任务的开始时间最差的等于上一个任务的结束时间

//TODO: Write code here
int n,m;
const int N=1e6+10;
int nums[N];
struct Node
{int s,e;
}node[N];
bool comp(Node a,Node b)
{if (a.e<=b.e) return true;return false;
}
signed main()
{cin>>n;for (int i=1;i<=n;i++){cin>>node[i].s>>node[i].e;}//结束时间最早int ans=0;int fg=0;sort(node+1,node+1+n,comp);for (int i=1;i<=n;i++){if (i==1)//第一个一定是可以的{ans++;fg=i;}else {if (node[i].s>=node[fg].e)	//如果下一个任务的开始在上一个任务的结束,则可以{fg=i;ans++;}}}cout<

P1090 [NOIP2004 提高组] 合并果子

P1090 [NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G

题目让我们求合并两堆的最小花费的总和, 因此我们很容易想到最小的两堆一起合并一定是最优的

我们便可以想到直接排序即可,花费小的在前面,然后前两个合并成一个,然后再把这个合并的放进去再排序,最后知道这个队列只有一个元素为止,因此我们每次所统计的两堆的和就是最后的答案。

这道题目的思想是很容易的。

但是如何做到 将两堆取出来合并后的值再放回去 ? 我们可以想到 堆排序。

进而想到堆排序的一个应用:优先队列

我们要制造 小顶堆,即堆顶元素是最小的,然后取出堆的前两个元素,合并后再插入堆

优先队列C++:priority_queue小顶堆形式:

priority_queue,greater> q;

大顶堆形式:

priority_queue q;

因此就解决了,当然你也可以手写堆,包含构建初始堆和堆的调整过程

//TODO: Write code here
int n,m;
const int N=1e5+10;
int nums[N];
signed main()
{cin>>n;priority_queue,greater> q;for (int i=1;i<=n;i++){int num;cin>>num;q.push(num);}if (q.size()==1)//特判{cout<=2){int num1=q.top();	//取出最小的q.pop();int num2=q.top();	//取出第二小的q.pop();int sum=num1+num2;ans+=sum;//注意统计resq.push(sum);	//合并后再插入堆}cout<

P3817 小A的糖果

P3817 小A的糖果

让我们求 在相邻的两个盒子的总数不超过 m 的情况下,至少需要吃的数量。

貌似我们可以进行排序? 从小到大,然后从左往右吃

不可以

我们注意到盒子是有先后顺序的,因此不能够改变位置(当然如果你会排序的做法则当我没说)

我们就直接贪心即可:

  1. 从左往右遍历,如果当前的盒子 i 的数量 + 后一个盒子 i+1 的数量 超过了规定 m,则我们一定需要在这两个中吃糖果,如何吃呢?

  1. 如果我们选择吃 i ,则 我们只会改变 i+1着一种情况,即 【i,i+1】是一组

  1. 但是如果我们吃 i+1,则我们不仅改变了【i,i+1】这一组,还可能改变 【 i+1,i+2】下一组,因此吃后面的一定是最优的(求最少的吃的数量)

  1. 遍历到后面的每个 i 盒子的时候,它的 i -1个位置的盒子数量 一定被上一种情况吃了。但是我们的第 1 个盒子怎么办呢,它可没有前一个?

  1. 我们直接错一下位即可, 让0(实际不存在,从1开始)号盒子 与 1 号成一组,然后我们吃后面的,这样不就吃到 1 号盒子了吗

//TODO: Write code here
int n,m;
const int N=1e5+10;
int nums[N];
signed main()
{cin>>n>>m;for (int i=1;i<=n;i++){cin>>nums[i];}int ans=0;for (int i=1;i<=n;i++){int a=i-1,b=i;	//从0开始,则保证了1号也能作为第二个盒子if (nums[a]+nums[b]>m){//尽量吃第二个ans+=nums[a]+nums[b]-m;//两个的总数 - 规定 = 这组中要吃的数量nums[b]=m-nums[a];	//全部吃第二个盒子}}cout<

P1106 删数问题

P1106 删数问题

在序列中删除 k 位后能够组成的最小的数。

貌似很简单?

直接从左往右,碰到一个 i 位置元素如果比 i-1 位置的元素大 ,则删除 i 位置的元素?

错误的!!

示例:

1 5 9 8 (2) ,删除两个,按照上面的思路,则删除 5 和 9,最后得到 18,但是实际上 15是最优的(删后两个)

1 2 6 5 9 7 (3),删除三个,按照上面的思路,则删除 2 6 和 9,得到了 1 5 7,但是实际上只要 2没被删除则其他的都比这个小。

那么怎么删呢? 观察一下式子,定义:比两边都大,则此位置为山峰

1 5 9(山峰) 8(山峰) :删除山峰,得到 1 5

为什么 8 也是山峰,9比8大啊,因为我们提前删除了9,因此 8 的前面是 5。

1 2 6(山峰) 5 9(山峰) 7(山峰):删除山峰,得到 1 2 5

答案:我们删除山峰位置的元素,则最后的数一定是最小的


实际上山峰在此题只需要表示为:比后面的数大即可

//TODO: Write code here
int n,k;
const int N=1e5+10;
char s[N];
int p=1;
bool vis[N];
signed main()
{cin>>s>>k;int len=strlen(s);//原始长度while (k--)//需要删除 k 个元素,每次循环一次则一定删除一个元素{for (int i=0;is[i+1]) //如果比后面的数字大,则删除 i 位置{//后面的位置元素依次覆盖过来,模拟删除 i 位置元素for (int j=i;j

相关内容

热门资讯

能不能给我《猫武士》第一本的第... 能不能给我《猫武士》第一本的第一个啊?呼唤野性的第一章如果能给我很多的话就更好啦!回答好我就给你一串...
高手们请帮忙翻译下下面的文章,... 高手们请帮忙翻译下下面的文章,急用,谢谢了!翻译成英文?
落跑甜心大结局是什么啊,具体的 落跑甜心大结局是什么啊,具体的男主角和女主角在一个没人的山坡举行婚礼仪式,仪式结束后,藏在山坡下的亲...
关于冬风的诗句 关于冬风的诗句千里黄云白日曛,北风吹雁雪纷纷。(唐·高适《别董大》)
林如海的介绍 林如海的介绍林如海,是清代小说《红楼梦》中的人物,巡盐御史,娶妻贾敏,林黛玉之父。
南阳有几个监狱,具体位置在哪,... 南阳有几个监狱,具体位置在哪,盗墓被判了大概五年,会在南阳那个监狱?哥,难道你就是传说中的摸金校尉。
有一本游戏小说主角是一个监狱的... 有一本游戏小说主角是一个监狱的人,还是秘密组织的人他得到一个神器叫沙盗王的戒指,上面有一段话是什么英...
形容不团结的成语有哪些? 形容不团结的成语有哪些?1、一盘散沙,、各奔东西2、,四分五裂、拳中掿沙3、七支八搭、勾心斗角4、自...
我们都一样年轻又彷徨中因为读了... 我们都一样年轻又彷徨中因为读了这本书抑郁症好了的是那一篇?保险杠铃声多多关照了我在家级的多吃点东西都...
小说炎景熙和陆沐擎最后在一起了... 小说炎景熙和陆沐擎最后在一起了吗熙带着花环和志焕一起拍了一张两个人的照片,那也是最后的一张照片,至此...
求有关化肥的绕口令。 求有关化肥的绕口令。1、初入江湖:化肥会挥发2、小有名气:黑化肥发灰,灰化肥发黑3、名动一方:黑化肥...
小喜鹊是怎样起床的 小喜鹊是怎样起床的早上大喜鹊从早上四点开始捕食喂小鸟,一直到晚上,早上小喜鹊和大喜鹊都醒了自身的生物...
贝贝小南瓜的生长资料? 贝贝小南瓜的生长资料?贝贝南瓜需要提前将种子浸泡一夜。幼苗定植一个月后,需要对土壤进行深耕,施肥。每...
苗族姑娘怎么画? 苗族姑娘怎么画? 画清楚,画好一些 找专业画家画啊,我就知道 , 兄弟壁画 挺好的,中国最大的了
求一篇科幻小说,男主被流放到一... 求一篇科幻小说,男主被流放到一颗有人形生物的星球,不断杀掉长相和地球人不同的外星生物男主被流放到一颗...
柳树怎么画 柳树怎么画柳树怎么画
快穿文女主姓宋叫什么歌,男主第... 快穿文女主姓宋叫什么歌,男主第一个位面姓林,叫什么不知道了,里面还有一个位面是腹黑王妃的?男主是那个...
哑舍同人文 哑舍同人文最好是上传的TAT~~~没有写哑舍同人文哦,怨念。。。。。。。。不过玄色大神在赤锁吧写了一...
陆墨宸简汐和刘雨彤的小说叫什么... 陆墨宸简汐和刘雨彤的小说叫什么名字宠婚:总裁在上
加法是什么意思? 加法是什么意思? 很高兴告诉你!加法: 把两个数合并成一个数的运算 把两个小数合并成一源粗带个小...