zoukankan      html  css  js  c++  java
  • 动态规划算法

      动态规划算法通常基于一个递推公式以及一个或多个初始状态,当前子问题的解由上一次子问题的解推出。

      在动态规划算法中有一个经典的例子就是硬币找零问题。

    1、问题描述

      如果我们有面值为1元、3元、5元的硬币若干,如何用最少的硬币凑够11元?

    2、思路分析

      基于动态规划的思想,我们可以从1元开始计算最少需要几个硬币,然后再求2元、3元、4元...

      首先,当i=0时,我们需要0个,即d(0)=0;

      当i=1时,只有面值为1元的硬币可用,d(1)=d(1-1)+1=1;

      当i=2时,仍然只有面值为1元的硬币可用,d(2)=d(2-1)+1=2;

      当i=3时,可用的硬币有1元和3元,如果我拿了1元的,我的目标就变成了凑够3-1=2元需要的最少硬币了,即d(3)=d(3-1)+1=3,另一个方案是我拿了一个3元的硬币,目标就是凑够3-3=0元需要的最少硬币数量,即d(3)=d(3-3)+1=1,所以综合两者方案的最小值d(3)=min{d(3-1)+1,d(3-3)+1}=1;

      当i=4时,可用的硬币有1元和3元,如果我拿了1元的,我的目标就变成了凑够4-1=3元需要的最少硬币了,即d(4)=d(4-1)+1=2,另一个方案是我拿了一个3元的硬币,目标就是凑够4-3=1元需要的最少硬币数量,即d(4)=d(4-3)+1=2,所以综合两者方案的最小值d(3)=min{d(4-1)+1,d(4-3)+1}=2;

      d(5)=1;

      d(6)=2;

      d(7)=3;

      d(8)=2;

      d(9)=3;

      d(10)=2;

      ...

      由上面可以退出公式,d(i)=min{d(i-vj)+1},其中i-vj>=0,vj表示第j个硬币的面值。

      现在,我们回到原题中,d(11)=min{d(11-vj)+1},其中vj可以取值为1,3,5。当vj=1时,d(11)=d(10)+1=3,当vj=3时,d(11)=d(8)+1=3,当vj=5时,d(11)=d(6)+1=3,所示d(11)=3。

    3、代码示例

      首先定义以下变量:

      values[] : 保存每一种硬币的币值的数组
      valueKinds :币值不同的硬币种类数量,即values[]数组的大小
      money : 需要找零的面值
      coinsUsed[] : 保存面值为 i 的纸币找零所需的最小硬币数

      当求解总面值为 i 的找零最少硬币数 coinsUsed[ i ] 时,将其分解成求解 coinsUsed[ i – cents]和一个面值为 cents 元的硬币,由于 i – cents < i , 其解 coinsUsed[ i – cents] 已经存在,如果面值为 cents 的硬币满足题意,那么最终解 coinsUsed[ i ] 则等于 coinsUsed[ i – cents] 再加上 1(即面值为 cents)的这一个硬币。代码如下:

    public class CoinsChange
    {
        /**  
         * 硬币找零:动态规划算法  
         *   
         */ 
        public static void makeChange(int[] values, int valueKinds, int money, int[] coinsUsed)
        {  
            coinsUsed[0] = 0; 
            int cents ;
            // 对每一块钱都找零,即保存子问题的解以备用,即填表  
            for (cents = 1; cents <= money; cents++) 
            {  
                // 当用最小币值的硬币找零时,所需硬币数量最多  
                int minCoins = cents;  
                // 遍历每一种面值的硬币,看是否可作为找零的其中之一  
                for (int kind = 0; kind < valueKinds; kind++)
                {               
                    // 若当前面值的硬币小于当前的cents则分解问题并查表  
                    if (values[kind] <= cents)
                    {  
                        int temp = coinsUsed[cents - values[kind]] + 1;  
                        // temp表示组合成cents需要的硬币数目 
                        if (temp < minCoins)
                        {  
                            minCoins = temp;  
                        }  
                    }  
                }  
                // 保存最小硬币数  
                coinsUsed[cents] = minCoins;  
            }  
            System.out.println("面值为 " + (money) + " 的最小硬币数 : " + coinsUsed[cents-1]);  
        }  
        public static void main(String[] args)
        {
            int[] coinValue = new int[] { 1,3,5 };  
            // 需要找零的面值  
            int money = 11;  
            // 保存每一个面值找零所需的最小硬币数,0号单元舍弃不用,所以要多加1  
            int[] coinsUsed = new int[money + 1];  
            makeChange(coinValue, coinValue.length, money, coinsUsed); 
        }
    }    
  • 相关阅读:
    洛谷1894 [USACO4.2]完美的牛栏The Perfect Stall
    洛谷2417 课程
    洛谷2860 [USACO06JAN]冗余路径Redundant Paths
    洛谷1983 车站分级
    BZOJ1178或洛谷3626 [APIO2009]会议中心
    BZOJ1179或洛谷3672 [APIO2009]抢掠计划
    CF Round #516 (Div. 2, by Moscow Team Olympiad)
    洛谷1262 间谍网络
    NOI导刊 2018河南郑州游记
    BZOJ1001或洛谷4001 [BJOI2006]狼抓兔子
  • 原文地址:https://www.cnblogs.com/xujian2014/p/5607305.html
Copyright © 2011-2022 走看看