算法06 贪心算法【C++实现】

我们可以扮演一个贪心的人,在金子、银、铁中选择装入背包带走的话,作为一个贪心的人,肯定要把价值最大化,优先要选择装载价值较高的金子。

目录

什么是贪心算法

证明方法

常见题型

常见题型解法

训练:小木船过河

解题思路

参考代码

训练:救援物资

解题思路

参考程序

训练:会场安排

解题思路

参考代码


什么是贪心算法

贪心算法(greedy algorithm),是用计算机来模拟一个「贪心」的人做出决策的过程。这个人十分贪婪,每一步行动总是按某种指标选取最优的操作。而且他目光短浅,总是只看眼前,并不考虑以后可能造成的影响。

可想而知,并不是所有的时候贪心法都能获得最优解,所以一般使用贪心法的时候,都要确保自己能证明其正确性。

证明方法

贪心算法有两种证明方法:反证法和归纳法。一般情况下,一道题只会用到其中的一种方法来证明。

反证法:如果交换方案中任意两个元素/相邻的两个元素后,答案不会变得更好,那么可以推定目前的解已经是最优解了。

归纳法:先算得出边界情况(例如n = 1)的最优解 F1,然后再证明:对于每个n,Fn+1都可以由 Fn推导出结果。

 

常见题型

最常见的贪心有两种。

我们将 XXX 按照某某顺序排序,然后按某种顺序(例如从小到大)选择。

我们每次都取 XXX 中最大/小的东西,并更新 XXX。(有时【XXX 中最大/小的东西】可以优化,比如用优先队列维护)

二者的区别在于一种是离线的,先处理后选择;一种是在线的,边处理边选择。

常见题型解法

排序解法:用排序法常见的情况是输入一个包含几个(一般一到两个)权值的数组,通过排序然后遍历模拟计算的方法求出最优值。

后悔解法:思路是无论当前的选项是否最优都接受,然后进行比较,如果选择之后不是最优了,则反悔,舍弃掉这个选项;否则,正式接受。如此往复。

训练:小木船过河

学校组织秋游的时候,小知和他的小伙伴准备乘坐小木船过河。

一只小木船最多只能乘坐两个人,且乘客的总重量不能超过小木船的最大承载量。为了尽量减少过河所需费用,所以小知希望找出可以让所有人过河的最少的小木船数量。现在给出小木船的最大承载量、过河总人数和每个人的重量。请你帮助小知计算出能让所有人过河的最少的小木船数量,并输出结果。

【输入描述】第一行有两个整数w,n,80<=w<=200,1<=n<=300,w为一条独木舟的最大承载量,n为人数;

接下来的一组数据为每个人的重量(不能大于船的承载量);

【输出描述】需要的最少独木舟的条数。

【输入样例1】85 6

5 84 85 80 84 83

【输出样例1】5

【输入样例2】100 5

50 50 90 40 60

【输出样例2】3

解题思路

为了能用最少的船帮助所有人过河,所以每次尽量运送最多的人是最好的。但是我们发现比较重的人有可能每次只能自己一个人过河,再加一个人重量就超过了最大载重。

于是我们就考虑到最佳的方案如下→

  • 对所有乘客体重升序(降序也可以)排序。
  • 因为一条独木舟最多只能乘坐两个人,且乘客的总重量不能超过独木舟的最大承载量,所以先将升序的体重首尾(最轻和最重的)相加,如果不超过最大载重,两人坐一艘船,即最优解,那么下一组比较次轻的和次重的。但是如果超过最大载重,则最重的那人坐一艘船,将最轻的和次重的重复步骤2。
  • 别忘记最后,当次轻和次重是同一人时,他一人坐一艘船。

参考代码

#include<iostream>
#include<algorithm>
using namespace std;
bool cmp(int a,int b){
    return a>b;
}

int main(){
    int n,a[350],t=0,w;
    cin>>w>>n;
    int j=n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    sort(a+1,a+1+n,cmp);
    int i=1;
    while(i<=j){
        if(a[i]+a[j]>w)
        {
           t++;
           i++;
        }
        else if(a[i]+a[j]<=w){
            t++;i++;j--;
         }   
     }
    cout<<t;
    return 0;
}

训练:救援物资

小知开着他的卡车准备给灾区送一些救援物资,他的卡车最大载重量为M公斤,同时他购买了N种食品,有食盐,白糖,大米等。已知第 i 种食品的拥有Wi公斤,其商品价值为Vi元/公斤,编程确定一个装货方案,使得装入卡车中的所有物品总价值最大。

【输入描述】第一行为卡车最大载重m公斤和n种食品;接下来每行表示第i种食品的重量Wi和价值Vi

【输出描述】输出总价值,结果保留两位小数

【输入样例】

10

3

3 4

4 5

2 6

【输出样例】44.00

解题思路

为了使装入卡车的货物价值最大,那么每次选取的必然是价值最大的货物。因此我们可以将每种货物的价值按照从大到小的顺序排序,然后每次依次选取价值大的货物。

在这之前需要判断重量是否超过承重,如果超过了,装满承重重量并结束,否则将货物全部装入,并且将车子承重减去已装入的重量,重复以上步骤直至结束。

参考程序

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
struct s{
    double w,v;//重量,价值
}a[105];

bool cmp(s p,s q){
    return p.v>q.v;
}

int main(){
    int m,n;
    cin>>m>>n;
    for(int i=1;i<=n;i++)
    cin>>a[i].w>>a[i].v;
    sort(a+1,a+n+1,cmp);
    double sum=0;
    for(int i=1;i<=n;i++){
        if(a[i].w<=m){
            sum+=a[i].w*a[i].v;
            m-=a[i].w;
        }
        else{
            sum+=m*a[i].v;
            break;
        }
    }
    printf("%.2f\n",sum);
    return 0;
}

训练:会场安排

ZY公司每天都有很多的会议要开,但是大会议室只有一个,如果有一个会议是在8点至9点开,则这个时间段不能安排别的会议,现在有一张会议安排计划的时间表,小知作为排班人员希望大会议室能够安排更多的会议,在保证更多会议的前提下,占用更少的时间以便会议室准备,请你来帮忙解决这个问题。

【输入描述】第一行输入n个会议(1<=n<=100),之后的每行分别记录这n个会议的开始和结束时间(整点,时间在0~24之间)。

【输出描述】第一行输出安排好之后的第一个会议的开始和结束时间;

第二行输出这间大会议室最多安排几个会议?

【输入样例】4

8 10

9 12

8 9

10 12

【输出样例】8 9

2

 

解题思路

解决这道题的思路是,我们先安排一个会议,再在安排了这个会议的前提下,在剩下的时间里尽可能多地安排其它会议。

这里我们不妨先安排整个会议室里的第一个会议,然后再在这个会议结束之后剩余的时间里安排尽可能多的会议。

这里正确的安排的方法是:

我们在安排完一个会议之后,要能使得剩下的时间尽可能多,这样就可以安排更多的会议。

所以我们每一次首先安排的会议是结束时间最早的那一个会议。因为结束时间最早的那个会议被先安排的话剩余时间是最多的。

 

参考代码

#include<iostream>
#include<algorithm>
using namespace std;

struct s{
    int st,ed;
}a[110];

bool cmp(s b,s c)
{
    if(b.ed!=c.ed)
        return b.ed<c.ed;
    return b.st>c.st;
}
int main()
{
    int n,t=0,j=0;
    cin>>n;
    for(int i=1;i<=n;i++)
            cin>>a[i].st>>a[i].ed;
    sort(a+1,a+1+n,cmp);
    for(int i=1;i<=n;i++)
    {
        if(a[i].st>=j)
        {
            j=a[i].ed;
            t++;
        }
    }
    cout<<a[1].st<<" "<<a[1].ed<<endl<<t;
    return 0;
}

从入门到算法,再到数据结构,查看全部文章请点击icon-default.png?t=N7T8http://www.bigbigli.com/ 

  • 24
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bigbigli_大李

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值