贪心算法,动态规划01背包思路讲解

文章介绍了贪心算法的概念,通过小A的糖果和部分背包问题实例展示如何在局部找最优解。同时讨论了贪心算法的优缺点,以及与动态规划在01背包问题上的区别。
摘要由CSDN通过智能技术生成

贪心算法

什么是贪心算法?

贪心算法,又名贪婪法,是寻找最优解问题的常用方法,这种方法模式一般将求解过程分成若干个步骤,但每个步骤都应用贪心原则,选取当前状态下最好/最优的选择(局部最有利的选择),并以此希望最后堆叠出的结果也是最好/最优的解。{看着这个名字,贪心,贪婪这两字的内在含义最为关键。这就好像一个贪婪的人,他事事都想要眼前看到最好的那个,看不到长远的东西,也不为最终的结果和将来着想,贪图眼前局部的利益最大化,有点走一步看一步的感觉。

由于贪心没有固定的模版,只是一种做题的思想,下面我们根据一些题目来了解贪心算法如何局部找最优。

 P3817 小A的糖果

题目描述

小 A 有 n 个糖果盒,第 i 个盒中有 ai​ 颗糖果。

小 A 每次可以从其中一盒糖果中吃掉一颗,他想知道,要让任意两个相邻的盒子中糖的个数之和都不大于 x,至少得吃掉几颗糖。

输入格式

输入的第一行是两个用空格隔开的整数,代表糖果盒的个数 n 和给定的参数 x。

第二行有 n 个用空格隔开的整数,第 i 个整数代表第 i 盒糖的糖果个数 ai​。

输出格式

输出一行一个整数,代表最少要吃掉的糖果的数量。

输入输出样例

输入 #1复制

3 3
2 2 2

输出 #1复制

1

输入 #2复制

6 1
1 6 1 2 0 4

输出 #2复制

11

输入 #3复制

5 9
3 1 4 1 5

输出 #3复制

0
说明/提示
样例输入输出 1 解释

吃掉第 2 盒中的一个糖果即可。


样例输入输出 2 解释

第 2 盒糖吃掉 66 颗,第 4 盒吃掉 22 颗,第 6 盒吃掉 33 颗。


数据规模与约定
  • 对于 30%30% 的数据,保证 n≤20,ai​,x≤100。
  • 对于 70%70% 的数据,保证 3n≤103,ai​,x≤105。
  • 对于 100%100% 的数据,保证 2≤n≤105,0≤ai​,x≤109。

首先,这道题我们分析一下该如何找到一个最好的值并修改它,他要求两个相邻的盒子相加<=x,那我们首先以样例一举例子:

2+2>3,我们把第一个2减去一个1,变成1 2,这样就满足条件。

继续 还是2 2 ,继续将第一个2减1,变成1 2 

最后数组变成了 1 1 2,最少吃2个糖果,显然这不是最优解,样例答案是1

我们再后头想一下,如果把2 2,后面这个2减1的话,就同时满足的左右两边,这个方法好像可行,这样将第二个数减去成了局部最优解,我们用程序来实现一下。

#include <stdio.h>
#include <stdlib.h>
long long a[100010];
int main()
{
    int n,x;
    long long sum=0;      
     scanf("%d%d",&n,&x);//  0 2 2 2  数据
     for(int i=1;i<=n;i++)//  0 1 2 3  下标
         {   scanf("%d",a+i);//这里可以用边读边写来
             if(a[i]+a[i-1]>x) //直接从第二个数开始,这里下标从1开始,方便相减
             {                
                 int k=a[i];  //记录当前的糖果数量
                 a[i]=x-a[i-1];// 用a[i]+a[i-1]<=x推出来
                 sum+=k-a[i];  //用之前的数据减去满足条件后的糖果,用sum记录吃掉多少
             }
         }
    printf("%lld",sum);
    return 0;
}

 P2240 【深基12.例1】部分背包问题

题目描述

阿里巴巴走进了装满宝藏的藏宝洞。藏宝洞里面有 (N≤100) 堆金币,第 i 堆金币的总重量和总价值分别是 ​(1≤mi​,vi​≤100)。阿里巴巴有一个承重量为 T(T≤1000) 的背包,但并不一定有办法将全部的金币都装进去。他想装走尽可能多价值的金币。所有金币都可以随意分割,分割完的金币重量价值比(也就是单位价格)不变。请问阿里巴巴最多可以拿走多少价值的金币?

输入格式

第一行两个整数 N,T。

接下来 N 行,每行两个整数 mi​,vi​。

输出格式

一个实数表示答案,输出两位小数

输入输出样例

输入 #1复制

4 50
10 60
20 100
30 120
15 45

输出 #1复制

240.00

这道题只需要算出每堆金币的占比,然后对占比进行排序,用for循环进行枚举,如果加上当前的容量大于T后使用berak跳出,用(T-当前背包的容量),得出占比后再乘以价值。

 

#include <stdio.h>
#include <stdlib.h>
float a[110][2];
float b[110][2];
int n,T;
int main()
{     scanf("%d%d",&n,&T);
     for(int i=0;i<n;i++)
     {
         scanf("%f%f",&a[i][0],&a[i][1]);
         b[i][0]=a[i][0]/a[i][1];//将占比算出来
         b[i][1]=a[i][1];
     }
    for(int i=0;i<n-1;i++)//冒泡排序,数据范围是<=100,所以不会爆,其实是不会用sort对二维数组排序......
    {
        for(int j=0;j<n-i-1;j++)
        {
            if(b[j][0]>b[j+1][0])
            {
                float t=b[j][0];
                b[j][0]=b[j+1][0];
                b[j+1][0]=t;
                t=b[j][1];
                b[j][1]=b[j+1][1];
                b[j+1][1]=t;
            }
        }
    }
    float k=0;//记录容量
    float sum=0;//记录价值
    int q;
    for(q=0;q<n;q++)//用for枚举,什么时候超出背包容量
    {
        k+=b[q][0]*b[q][1];//b[q][0]*b[q][1]这个相乘后是最开始的每堆金币的容量,开始算占比,算绕回去了
        sum+=b[q][1];//对价值统计
        if(k>T)
            break;
    }
      if(q<n)
      {
        k-=b[q][0]*b[q][1];//因为加了容量后超出范围了,所以减去
        sum-=b[q][1];//同样
        int p=T-k;//这里用来记录背包还剩多少容量,用q记录的金堆给他塞满
        k+=p;
        sum+=p/(b[q][0]*b[q][1])*b[q][1];//这里算价值,(b[q][0]*b[q][1])是在b[q][1]占了多少比值
      }

    printf("%.2f",sum);//输出
    return 0;
}

上面讲完了部分背包,我们来看看完整的背包问题,01背包----------头大,只能用动态规划来写,贪心写不来 。

这里再总结一下贪心的优缺点

总结:贪心算法的优缺点

优点:简单,高效,省去了为了找最优解可能需要穷举操作,通常作为其它算法的辅助算法来使用;

缺点:不从总体上考虑其它可能情况,每次选取局部最优解,不再进行回溯处理,所以很少情况下得到最优解。

 动态规划-01背包

问题描述

有n个物品,它们有各自的体积和价值,现有给定容量的背包,如何让背包里装入的物品具有最大的价值总和?

现有四个物品,小偷背包总容量为8,怎么可以偷得价值最多的物品。

物品编号: 1 2 3 4

物品重量: 2 3 4 5

物品价值: 3 4 5 8

这里很明显偷2 4 价值最高为12,那么我们如何来分析这个题目呢?

首先,我们有8个容量的空间,我们可以先找到物品编号为4的,这里有两个选择偷还是不偷,

 

这里我们来看这张图,当我们到第四编号时,有偷和不偷两种选择,我们先来看看偷得情况,首先偷得话先检查背包容量是否足够,8肯定够了,所以我们往下走,这时到编号3了,这时又有两种选择,偷还是不偷,明显这里只能不偷,背包容量不够了,到编号2了,这时还是偷还是不偷,选择偷,背包容量空了,选择不偷到编号1去了,还是偷还是不偷,偷得话前面没物品了只能结束,偷完后,没物品了,所以结束了,这一遍模拟下来,动态规划其实跟递推差不多。------这里就进行思路讲解吧,代码就不贴了,晚安。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值