贪心算法例题与讲解(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

相关内容

热门资讯

魁拔4发蛮吉是魁拔了吗? 魁拔4发蛮吉是魁拔了吗?发现了,你可以去看魁书2,感情线也明了了,我会骗你吗?谁知道,去问王川呀
找一本小说男主好像姓凤,女主是... 找一本小说男主好像姓凤,女主是一对双胞胎的姐姐,叫什么就忘了,男二号叫龙飞,和双胞胎妹妹相爱的女主姓...
明朝精彩故事 明朝精彩故事喜欢明朝的故事,建议看【明朝那些事儿】用现代的语言说明朝的故事,你会喜欢的,里面语言很幽...
用过‘千万别学英语’的请进 用过‘千万别学英语’的请进我觉得这书没什么意思,有时候任何东西都是只适合一部分人的。我每天听那些磁带...
学英语时英美式发音混在一起了怎... 学英语时英美式发音混在一起了怎么办?哪种发音好?学的词汇是英式发音,但是很多听力却是美式发音的,怎么...
"我不是个稻草人,不能说不能动... "我不是个稻草人,不能说不能动"是哪首歌里的?稻草人,林志颖唱的林志颖演唱的 稻草人林志颖的稻草人
恶魔猎手有什么那么厉害,高手看... 恶魔猎手有什么那么厉害,高手看中他那点厉害恶魔猎手有什么那么厉害,高手看中他那点厉害速度技能都很好。...
请问这样的短信该怎样回 请问这样的短信该怎样回夸奖夸奖,久闻阁下大名,今日一见果然名不虚传。肥硕如象,真是过犹而不及。小生我...
女上司总来撩我怎么办,我也会把... 女上司总来撩我怎么办,我也会把持不住的找借口或是直接反撩她,我猜你也不敢,可能对你有意思,没女朋友就...
为什么良渚文化可以证明中华文明... 为什么良渚文化可以证明中华文明五千年,而龙山文化等其他文化不能证明?因为良渚文化有不少文化遗产可以证...
炎亚纶的资料。 炎亚纶的资料。去网上搜 炎亚纶
男主重生重进宗门 不是都市小说... 男主重生重进宗门 不是都市小说 以前在师傅手下学药后来和师姐好上了重生小说一般都不会放过师姐。。。
万章曰:"尧以天下与... 万章曰:"尧以天下与舜,有诸?(孟子的学生)万章问(孟子):“尧把天下授与舜,有这回事吗?” 问的是...
写近义词饱胀对什么清早对什么依... 写近义词饱胀对什么清早对什么依然对什么?饱胀对饱满,清早对清晨,依然对依旧饱胀的近义词: 饱足 饱满...
梦到在上课 梦到在上课老师你也敢戏弄?别说给小鞋你穿,紧紧鞋带都够你受的。这是你潜意识里发泄心理压力,而达到心理...
怎么写一个人很温柔,写一段话 怎么写一个人很温柔,写一段话我不知道那些鸿雁究竟是怎样掠夺了我忧郁无助的天空。就像你撩动温柔浸渍了我...
中风了应该怎样处理? 中风了应该怎样处理?中风多由脑血管疾病引发,是一种常见急症。处理原则是:保持病人安静,让病人卧下,将...
盘点2021高评分的古装剧,哪... 盘点2021高评分的古装剧,哪部剧最让你感到意难平?杨幂的斛珠夫人 。在这部剧的结局当中,男一男二,...
奔字的笔顺笔画顺序 奔字的笔顺笔画顺序汉字: 奔 读音: bèn bēn 部首: 大 笔画...
智商跟性格有关吗 智商跟性格有关吗有关啊,有相关的研究,但是相关未必是因果,可能是因为智力高所以才有的那些性格或者别的...