Day_29,贪心算法(详细描述及0-1背包问题/分数背包)

1、贪心算法的设计过程
2、如何证明贪心算法能否求解一个最优化问题
3、0-1背包问题/分数背包

贪心算法的设计过程

动态规划算法已在上篇文章中阐述并且使用了 动态规划及贪心算法求解活动选择,下面将分析是如何设计贪婪算法的,将通过下面5个点来说明,上篇文章就是按照下述方法实现贪心算法的。

1.确定最优子结构
2.设计递归算法
3.做出贪心选择
4.证明贪心选择是安全的(不影响最优子结构)
5.做一个迭代算法求出最优活动选择

按照上面顺序对应到活动选择,分别是
1.最优子结构: c[i,j] = c[i,k]+c[k,j]+1
2.递归算法: c[i,j] = max(c[i,j],c[i,k]+c[k,j]+1)
3.贪心选择: 选择最早结束的活动,如上篇文章中的贪心运行代码
4.证明贪心选择是安全的: 如上篇文章中的定理16.1的证明(其实稍微理解一下也能够判断得出是正确的┑( ̄Д  ̄)┍)
5.贪心的迭代算法: A=A∪ak,S[ak]≥F[Alast],其中A是选择出来的活动集,S是开始时间,F是结束时间

上述步骤可以简化成下面三个步骤:
1.最优化问题做出一次选择之后,只剩下一个子问题需要求解
2.证明贪心选择是安全的
3.做出贪心选择之后,最优解与贪心选择组合可以得到原问题的最优解,即得到最优子结构

按照上面顺序对应到活动选择,分别是
1.由原本的 c[i,j] = max(c[i,j],c[i,k]+c[k,j]+1) 需要考察所有的选择(i~j的所有组合),变成只需要考虑 哪个活动最早结束
2.如上篇文章中的定理16.1的证明(其实稍微理解一下也能够判断得出是正确的┑( ̄Д  ̄)┍)
3.A=A∪ak,S[ak]≥F[Alast]

虽然步骤简化了,但是要始终记得,在每一个贪心算法背后,都有一个更复杂的动态规划

如何证明贪心算法能否求解一个最优化问题

有两个要素: 贪心选择性质最优子结构
上面这两个要素并不能确定贪心算法一定能解决最优化问题,但是没有一定不能。
贪心选择性质: 可以通过做出局部最优选择来构造全局最优解。👈这个很重要,没有的话不能用贪心算法哦 也就是说,我们不需要考虑子问题的解,只需要做出当时看起来是最好的选择。
最优子结构: 同动态规划,即一个问题的最优解包含其子问题的最优解,我们则称他有最优子结构。

贪心选择和动态规划的适用问题类型

动态规划和贪心选择都有着最优子结构,那他们又有何区别呢?看下面两个问题
在这里插入图片描述
现在假设含有这样两个情况
“商品1: 60RMB,重10kg;商品2: 100RMB,重20kg;商品1: 120RMB,30kg;”
“金沙: 60RMB,重10kg;银沙: 100RMB,重20kg;铜沙: 120RMB,30kg;”

上述两个问题是不是很像!他们都有着最优子结构,但是他们
0-1背包问题: 能使用动态规划算法,但是不能使用贪心算法。
咱们来思考一下,如果咱们用贪心算法,那他贪心的核心是什么呢?
拿价值最高的东西?可能背包装不满导致价值非最大
拿最多的东西?价值高的东西难道不香吗?
分数背包问题: 贪心算法,当然也就可以用动态规划的方法

下面分别给出上面两个问题对应的代码:
//dp[i][w]表示0~i-1个物品中选择出来的最大价值,背包剩余承重为w
//第i个物品我们有两种状态,选和不选
//不选的话问题变成: dp[i-1][w] ,即背包剩余承重不变
//选的话问题变成: dp[i-1][w-wi] ,即背包剩余承重少了wi
//所以状态方程就是: dp[i][w] = max(dp[i-1][w-wi]+vi ,dp[i-1][w])
//即dp[i][w] = max(dp[i-1][w–weight[i-1]]+vi ,dp[i-1][w])
0-1背包问题:

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <vector>
using namespace std;

int zeroOneBag(int *value,int *weight,int bagCap,int num)
{
    vector<vector<int>> dp(num+1,vector<int>(bagCap+1));
    for (int i=0;i<bagCap;i++)
    {
        dp[0][i]=0;
    }
    for (int i=0;i<num;i++)
    {
        dp[i][0]=0;
    }
    for (int i=1;i<=num;i++)
        for (int w=1;w<=bagCap;w++)
            if (weight[i]>w)
                dp[i][w] = dp[i-1][w];
            else
                dp[i][w] = max(dp[i-1][w],dp[i-1][w-weight[i]]+value[i]);
    return dp[num][bagCap];
}

int main()
{
    int value[3] = {60,100,120};
    int weight[3] = {10,20,30};
    int bagCapacity = 50;
    int result = zeroOneBag(value,weight,bagCapacity,sizeof(value)/sizeof(*value));
    cout<<result;
}

运行结果如下

// 拿均价尽量高的物品
分数背包问题:

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <vector>
using namespace std;

int fractionBag(int *value,int *weight,int bagCap,int num)
{
    int totalValue = 0;
    int tempCap = 0;
    int ave[num]={};
    for (int i=0;i<num &&tempCap<bagCap;i++)
        if (weight[i]+tempCap<=bagCap)
        {
            totalValue+= value[i];
            tempCap+= weight[i];
        }
        else
        {
            totalValue+= (bagCap-tempCap)*(value[i]/weight[i]);
            tempCap+= bagCap-tempCap;
        }
    return totalValue;
}

int main()
{
    int value[3] = {60,100,120};
    int weight[3] = {10,20,30};
    int bagCapacity = 50;
    int result = fractionBag(value,weight,bagCapacity,sizeof(value)/sizeof(*value));
    cout<<result;
}

在这里插入图片描述

结文鸡汤:真正的实力不是体现在你状态好的时候能做到100分,而是在你状态不佳的时候也能做到100分((╯‵□′)╯︵┻━┻,说状态不好都是接口!)

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值