• 两个动态规划的经典问题


    硬币问题#

    Copy
    问题描述:设有n种不同面值的硬币,各硬币的面值存于数组T[1:n]中。现要用这些面值的硬币来找钱。可以使用的各种面值的硬币个数存于数组Coins[1:n]中。 对任意钱数0≦m≦20001,设计一个用最少硬币找钱m的方法。 算法设计:对于给定的1≦n≦10,硬币面值数组T和可用使用的各种面值的硬币个数数组Coins,以及钱数m,0≦m≦20001,计算找钱m的最少硬币数。

    我们再回想一下实现动态规划的前提:

    • 最优化原理:如果问题的最优解所包含的子问题的解也是最优的,就称该问题具有最优子结构,即满足最优化原理。
    • 无后效性:即某阶段状态一旦确定,就不受这个状态以后决策的影响。也就是说,某状态以后的过程不会影响以前的状态,只与当前状态有关;
    • 有重叠子问题:即子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到(该性质并不是动态规划适用的必要条件,但是如果没有这条性质,动态规划算法同其他算法相比就不具备优势)。

    在做这道题之前,我们先来看一个样例:

    问题描述#

    假设有 1 元,3 元,5 元的硬币若干(无限),现在需要凑出 11 元,问如何组合才能使硬币的数量最少?

    问题分析#

    乍看之下,我们简单的运用一下心算就能解出需要 2 个 5 元和 1 个 1 元的解。当然这里只是列出了这个问题比较简单的情况。当硬币的币制或者种类变化,并且需要凑出的总价值变大时,就很难靠简单的计算得出结论了。贪心算法可以在一定的程度上得出较优解,但不是每次都能得出最优解。

    这里运用动态规划的思路解决该问题。按照一般思路,我们先从最基本的情况来一步一步地推导。

    我们先假设一个函数 d(i) 来表示需要凑出 i 的总价值需要的最少硬币数量。

    1.当 i = 0 时,很显然我们可以知道 d(0) = 0。因为不要凑钱了嘛,当然也不需要任何硬币了。注意这是很重要的一步,其后所有的结果都从这一步延伸开来。
    2.当 i = 1 时,因为我们有 1 元的硬币,所以直接在第 1 步的基础上,加上 1 个 1 元硬币,得出 d(1) = 1。
    3.当 i = 2 时,因为我们并没有 2 元的硬币,所以只能拿 1 元的硬币来凑。在第 2 步的基础上,加上 1 个 1 元硬币,得出 d(2) = 2。
    4.当 i = 3 时,我们可以在第 3 步的基础上加上 1 个 1 元硬币,得到 3 这个结果。但其实我们有 3 元硬币,所以这一步的最优结果不是建立在第 3 步的结果上得来的,而是应该建立在第 1 步上,加上 1 个 3 元硬币,得到 d(3) = 1。

    ...

    接着就不再举例了,我们来分析一下。可以看出,除了第 1 步这个看似基本的公理外,

    其他往后的结果都是建立在它之前得到的某一步的最优解上,加上 1 个硬币得到。

    得出:d(i) = d(j) + 1

    这里 j < i。通俗地讲,我们需要凑出 i 元,就在凑出 j 的结果上再加上某一个硬币就行了。

    那这里我们加上的是哪个硬币呢。嗯,其实很简单,把每个硬币试一下就行了:

    假设最后加上的是 1 元硬币,那 d(i) = d(j) + 1 = d(i - 1) + 1。
    假设最后加上的是 3 元硬币,那 d(i) = d(j) + 1 = d(i - 3) + 1。
    假设最后加上的是 5 元硬币,那 d(i) = d(j) + 1 = d(i - 5) + 1。

    我们分别计算出 d(i - 1) + 1d(i - 3) + 1d(i - 5) + 1 的值,取其中的最小值,即为最优解,也就是 d(i)

    最后公式:

    img

    Copy
    int main() { int n, m, dp[20002]; cin >> n;//输入硬币种类 int value[n + 1], coins[n + 1]; for (int i = 1; i <= n; i++) cin >> value[i] >> coins[i];//分别输入面值与数量 cin >> m;//最后找钱m for (int i = 1; i < 20002; i++) dp[i] = 1000000;//一维数组存储结果初定义 dp[0] = 0; for (int i = 1; i <= n; i++)//面值种类 for (int j = 1; j <= coins[i]; j++)//面值数量 for (int k = m; k >= value[i]; k--) dp[k] = min(dp[k - value[i]] + 1, dp[k]);//核心(先找面值大的硬币,) cout << (dp[m] < 1000000 ? dp[m] : -1) << endl;//问题无解输出-1 return 0; }

    背包问题#

    5个物体,重量分别为3,5,7,8,9,价值分别为4,6,7,9,10,背包容量为22,物体不能分割,求装入背包物体最大价值,写出求解过程。

    Copy
    //题目: //求解下面的背包问题。有5个体积是3,5,7,8和9,价值为4,6,7,9和10的物品,背包的容量是22。 #include using namespace std; #define NUM 5 //物体的个数 #define CAPCITY 22 //背包的容量 int main(){ //初始化每个物体的体积以及价值,注意数组第一个元素是0 int volume[CAPCITY+1]={0,3,5,7,8,9}; int value[NUM+1]={0,4,6,7,9}; //新建一个二维表用于存储每一步的结果 int result[NUM+1][CAPCITY+1]; //初始化二维结果表,这里的初始化是指记录每次选择(每一步)的最大价值,注意这里的选择不是穷举啊,算法的逻辑不清楚的话可以看 //算法图解 for(int i=0;i<=NUM;i++){result[i][0]=0;} for(int j=0;j<=CAPCITY;j++){result[0][j]=0;} for(int i=1;i<=NUM;i++){ for(int j=1;j<=CAPCITY;j++){ result[i][j]=result[i-1][j]; if(volume[i]<=j){ result[i][j]=max(result[i][j],result[i-1][j-volume[i]]+value[i]); } } } //将包含选择结果的表打印下来 for(int k=0;k<=NUM;k++){ for(int l=0;l<=CAPCITY;l++){ cout<if(result[k][l]>9) cout<<' '; else cout<<" "; } cout<<endl; } cout<<"由上表可知,背包内可装下的物品的最大价值是:"<endl; system("pause"); return 0; }

    image

  • 相关阅读:
    视频剪辑的视频素材哪里下载?
    微电网与电动车蓄电池优化(Matlab代码实现)
    C++算法题 - 矩阵
    绘画系统(01):【纲】Paint System[官翻]
    深入浅出InnoDB Locking
    sharding分片键 groovy公式带有非数字字符启动失败
    微机原理:逻辑运算指令、移位指令
    Unity 帧同步游戏解决方案梳理
    鸿蒙HarmonyO实战-ArkUI动画(组件内转场动画)
    oracle 19c打补丁到19.14
  • 原文地址:https://www.cnblogs.com/galaxyStar/p/16885537.html