贪心算法与动态规划
案例一:
买东西时需要找给收银员六元三角,有两元五角、一元、一角的硬币可供选择,且要求找给的硬币数最小。
分析:
既然需要找给的硬币数目最小,那么可以通过每次在不超过找零金额的情况下选择面值最大的硬币:
找零金额:6.3 选取硬币:2.5
找零金额:3.8 选取硬币:2.5
找零金额:1.3 选取硬币:1.0
找零金额:0.3 选取硬币:0.1
找零金额:0.2 选取硬币:0.1
找零金额:0.1 选取硬币:0.1
至此,通过这种方法用六枚硬币完成了找零,在这一过程中,每次的选择都是当前情况下的最优选择,对于这个案例来说,六枚硬币即是整体的最优解。
案例二:
买东西时需要找给收银员一元五角,有一元一角、五角、一角的硬币可供选择,且要求找给的硬币数最小。
依然按照案例一的思路:
找零金额:1.5 选取硬币:1.1
找零金额:0.4 选取硬币:0.1
找零金额:0.3 选取硬币:0.1
找零金额:0.2 选取硬币:0.1
找零金额:0.1 选取硬币:0.1
最终使用了五枚硬币。考虑选择三枚0.5的硬币,五枚硬币显然不是最优解,案例一的思路在这里得出了错误的结果。
此时可以采取另外一种思路:
设0.1、0.5、1.1为第1、2、3种硬币的面值,c[i,j]表示可以用第0、……i种硬币对金额为j的金钱进行找钱所需要的最少硬币数。(注意:这里的i指硬币种类数而非硬币枚数,i表示可用硬币种类数,j表示需要找回的零钱)
那么对于第i种硬币来讲有两种选择:
一种是不使用第i种硬币,那么c[i,j]=c[i-1,j],还有一种则是使用第i种硬币,那么c[i,j]=c[i,j-value[i]]+1,(这里的value[i]指第i种硬币的面值,而+1则意指使用了一枚第i种硬币)
对于每一个c[i,j]来讲,它的值无外乎于c[i-1,j]和c[i,j-value[i]]+1这两种情况,而问题要求使用的硬币数最少,即可总结表达式:c[i,j]=min{c[i-1,j],c[i,j-value[i]]+1} (其称为特征转移方程)
对于任意的i,c[i,0]都为0,因为用i种硬币对金额0进行找钱,只需要0枚硬币,对于任意的j,c[0,j]意为用0种硬币对金额为j的金钱进行找零,其属于未定义的状态,应该被赋值为+∞(参考c[i,j]=min{c[i-1,j],c[i,j-value[i]]+1})
//其上两种情况称作边界条件
同时还应该考虑特殊情况,即value[i]>j,那么第i枚硬币面值大于金额j,则显然不能用第i种硬币找零。
伪代码如下:
总结:
在案例一和案例中,总共采取了两种思想来解决最小硬币数找零的问题。
第一种思想称之为贪心算法
第二种思想则称之为动态规划
上方所讨论的问题是01背包的一种
01背包是在M件物品取出若干件放在空间为W的背包里,每件物品的体积为W1,W2至Wn,与之相对应的价值为P1,P2至Pn。01背包是背包问题中最简单的问题。01背包的约束条件是给定几种物品,每种物品有且只有一个,并且有权值和体积两个属性。在01背包问题中,因为每种物品只有一个,对于每个物品只需要考虑选与不选两种情况。如果不选择将其放入背包中,则不需要处理。如果选择将其放入背包中,由于不清楚之前放入的物品占据了多大的空间,需要枚举将这个物品放入背包后可能占据背包空间的所有情况。
在找零问题中,每种硬币都只有选与不选两种情况,可归于01背包问题中。
动态规划算法有自顶向下的备忘录法和自底向上两种,而找零问题中所采取的是自底向上的形式。
动态规划的实质是分治思想和解决冗余,动态规划算法是将问题分解为更小的、相似的子问题,并存储子问题的解而避免计算重复的子问题,以解决最优化问题的算法策略。
例如在用递归计算斐波那契数列时
在输入数字6时
在这个递归中,每个子节点都会被执行一次,其中fb(2)被执行了5次,但我们可以将执行过的子节点保存起来,后面要用到的时候直接查表调用就可以节约大量的时间。
●自顶向下的备忘录法
伪代码如下
●自底向上
//一道贪心算法题目
P5019 铺设道路
题目描述
春春是一名道路工程师,负责铺设一条长度为 nn 的道路。
铺设道路的主要工作是填平下陷的地表。整段道路可以看作是 nn 块首尾相连的区域,一开始,第 ii 块区域下陷的深度为 d_idi 。
春春每天可以选择一段连续区间[L,R][L,R] ,填充这段区间中的每块区域,让其下陷深度减少 11。在选择区间时,需要保证,区间内的每块区域在填充前下陷深度均不为 00 。
春春希望你能帮他设计一种方案,可以在最短的时间内将整段道路的下陷深度都变为 00 。
输入格式
输入文件包含两行,第一行包含一个整数 nn,表示道路的长度。 第二行包含 nn 个整数,相邻两数间用一个空格隔开,第ii 个整数为 d_idi 。
输出格式
输出文件仅包含一个整数,即最少需要多少天才能完成任务。
买东西时需要找给收银员六元三角,有两元五角、一元、一角的硬币可供选择,且要求找给的硬币数最小。
分析:
既然需要找给的硬币数目最小,那么可以通过每次在不超过找零金额的情况下选择面值最大的硬币:
找零金额:6.3 选取硬币:2.5
找零金额:3.8 选取硬币:2.5
找零金额:1.3 选取硬币:1.0
找零金额:0.3 选取硬币:0.1
找零金额:0.2 选取硬币:0.1
找零金额:0.1 选取硬币:0.1
至此,通过这种方法用六枚硬币完成了找零,在这一过程中,每次的选择都是当前情况下的最优选择,对于这个案例来说,六枚硬币即是整体的最优解。
案例二:
买东西时需要找给收银员一元五角,有一元一角、五角、一角的硬币可供选择,且要求找给的硬币数最小。
依然按照案例一的思路:
找零金额:1.5 选取硬币:1.1
找零金额:0.4 选取硬币:0.1
找零金额:0.3 选取硬币:0.1
找零金额:0.2 选取硬币:0.1
找零金额:0.1 选取硬币:0.1
最终使用了五枚硬币。考虑选择三枚0.5的硬币,五枚硬币显然不是最优解,案例一的思路在这里得出了错误的结果。
此时可以采取另外一种思路:
设0.1、0.5、1.1为第1、2、3种硬币的面值,c[i,j]表示可以用第0、……i种硬币对金额为j的金钱进行找钱所需要的最少硬币数。(注意:这里的i指硬币种类数而非硬币枚数,i表示可用硬币种类数,j表示需要找回的零钱)
那么对于第i种硬币来讲有两种选择:
一种是不使用第i种硬币,那么c[i,j]=c[i-1,j],还有一种则是使用第i种硬币,那么c[i,j]=c[i,j-value[i]]+1,(这里的value[i]指第i种硬币的面值,而+1则意指使用了一枚第i种硬币)
对于每一个c[i,j]来讲,它的值无外乎于c[i-1,j]和c[i,j-value[i]]+1这两种情况,而问题要求使用的硬币数最少,即可总结表达式:c[i,j]=min{c[i-1,j],c[i,j-value[i]]+1} (其称为特征转移方程)
对于任意的i,c[i,0]都为0,因为用i种硬币对金额0进行找钱,只需要0枚硬币,对于任意的j,c[0,j]意为用0种硬币对金额为j的金钱进行找零,其属于未定义的状态,应该被赋值为+∞(参考c[i,j]=min{c[i-1,j],c[i,j-value[i]]+1})
//其上两种情况称作边界条件
同时还应该考虑特殊情况,即value[i]>j,那么第i枚硬币面值大于金额j,则显然不能用第i种硬币找零。
伪代码如下:
if(i==0) c[i][j]=max;
if(j==0) c[i][j]=0;
}else{
if(j>=value[i]){
c[i][j]=min(c[i-1][j],1+c[i][j-value[i]])
}else{
T[i][j]=T[i-1][j];
}
}
总结:
在案例一和案例中,总共采取了两种思想来解决最小硬币数找零的问题。
第一种思想称之为贪心算法
第二种思想则称之为动态规划
上方所讨论的问题是01背包的一种
01背包是在M件物品取出若干件放在空间为W的背包里,每件物品的体积为W1,W2至Wn,与之相对应的价值为P1,P2至Pn。01背包是背包问题中最简单的问题。01背包的约束条件是给定几种物品,每种物品有且只有一个,并且有权值和体积两个属性。在01背包问题中,因为每种物品只有一个,对于每个物品只需要考虑选与不选两种情况。如果不选择将其放入背包中,则不需要处理。如果选择将其放入背包中,由于不清楚之前放入的物品占据了多大的空间,需要枚举将这个物品放入背包后可能占据背包空间的所有情况。
在找零问题中,每种硬币都只有选与不选两种情况,可归于01背包问题中。
动态规划算法有自顶向下的备忘录法和自底向上两种,而找零问题中所采取的是自底向上的形式。
动态规划的实质是分治思想和解决冗余,动态规划算法是将问题分解为更小的、相似的子问题,并存储子问题的解而避免计算重复的子问题,以解决最优化问题的算法策略。
例如在用递归计算斐波那契数列时
int fib(int n)
{
if(n<=0)
return 0;
if(n==1)
return 1;
return fib( n-1)+fib(n-2);
}
在输入数字6时
●自顶向下的备忘录法
伪代码如下
int fib(n,memo){
if(memo[n]!=None)
return memo[n];
if(n==1||n==2)
result=1;
else
result=fib(n-1)+fib(n-2);
memo[n]=result;
return result;
}
●自底向上
int fib(n,memo){
n[0]=n[1]=1;
for(int i=2;i<n;i++){
n[i]=n[i-1]+n[i-2];
}
}
P5019 铺设道路
题目描述
春春是一名道路工程师,负责铺设一条长度为 nn 的道路。
铺设道路的主要工作是填平下陷的地表。整段道路可以看作是 nn 块首尾相连的区域,一开始,第 ii 块区域下陷的深度为 d_idi 。
春春每天可以选择一段连续区间[L,R][L,R] ,填充这段区间中的每块区域,让其下陷深度减少 11。在选择区间时,需要保证,区间内的每块区域在填充前下陷深度均不为 00 。
春春希望你能帮他设计一种方案,可以在最短的时间内将整段道路的下陷深度都变为 00 。
输入格式
输入文件包含两行,第一行包含一个整数 nn,表示道路的长度。 第二行包含 nn 个整数,相邻两数间用一个空格隔开,第ii 个整数为 d_idi 。
输出格式
输出文件仅包含一个整数,即最少需要多少天才能完成任务。
