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分((╯‵□′)╯︵┻━┻,说状态不好都是接口!)