动态规划 如何用最少的硬币枚数找钱?

遇到问题后,可以先用回溯法解决,虽然时间复杂度比较高,但是容易进行思考和分析。

之后再根据动态规划的1个模型,3个特性,看是问题是否满足,如果满足,再用2个方法找到问题的动态规划版本,降低时间复杂度(一般会提升一些空间复杂度)

 

public class Coins {
    //动态规划特点:
    //1个模型,3个特性,2个方法
    //1个模型,3个特性
    //满足了就可以用动态规划求解
    //1模型:阶段决策最优解模型
    //3特性:
    //1.重复子问题 (不同的决策序列,到某个阶段后,会遇到相同的状态)
    //2.无后效性
    //2.1.只关心解决当前阶段子问题,不需要知道之前的状态是怎么推导出来的
    //2.2.前面解决的问题,不受到后面解决的子问题影响其状态
    //3.最优子结构(一个问题的最优解,包含子问题的最优解,要求这个问题的最优解,就化简成了,求这个问题所有子问题的最优解,如果都求解到了,那么这个问题的最优解也就求解到了)
    //
    //2个方法
    //1.状态表
    //回溯法暴力求解(可以配合’子问题状态的记忆‘来修枝)-> 确定状态(子问题由哪几个主要参数决定) - 画递归树 - 找重复子问题 - 画状态转移表(一般是2维表) - 填状态表 - 按照填写过程的逻辑code (- 在此基础上优化空间复杂度 - 状态方程)
    //2.状态方程(类似递归时候写的 递推公式)
    //找最优子结构 -> 写动态方程 - code

    //回溯,动态规划,贪心,分治
    //分类:
    // 1.回溯,动态规划,贪心 (求最优解)
    // 2.分治
    // 最初分析问题的时候他们都可以用递归的编程思想(方法)来求出时间复杂度相对较高的初期解决方案
    //能解决的问题 :回溯>动态规划>贪心
    //时间复杂度   :回溯>动态规划>贪心
    //            2^n n*m


    //1.暴力回溯法求解 O(n^m)//2.用动态规划找给定面值的硬币(最少硬币枚数)
    //返回-1 表示找不开
    public static int getCoinCount(int money, int[] coins, int n) {
        if (check(money, coins, n))
            return -1;

        int[] state = new int[money + 1];       //下标:[i]=剩余需要找i元, 值:需要找的硬币枚数
        int[] coinValues = new int[money + 1];  //下标:i剩余需要找i元,值:需要找的每个硬币的币值
        for (int i = 0; i < money + 1; i++)
            state[i] = -1;
        state[money] = 0;
        for (int i = money; i >= 0; i--) {
            if (state[i] == -1)
                continue; //当前状态无效(没有任何一种找法,可以得到剩余i元的情况)
            for (int j = 0; j < n; j++) {
                int toMoney = i - coins[j];
                if (toMoney < 0)
                    continue; //找过头了
                int coinCount = state[i] + 1;
                if (state[toMoney] == -1 || state[toMoney] > coinCount) {
                    state[toMoney] = coinCount;
                    coinValues[toMoney] = coins[j];
                    if (toMoney == 0) {
                        System.out.println("toMoney==0  " + coinCount);
                        //其实,可以break出最外层循环了,以最少的硬币枚次找到了合适的钱
                    }
                }
            }
        }
        if (coinValues[0] != 0) {
            int i = 0;
            while (coinValues[i] != 0) {
                System.out.println("coinValue(面值): " + coinValues[i]);
                i += coinValues[i];
                if (i > money - 1)
                    break;
            }
        }
        return state[0];
    }

    private static boolean check(int money, int[] coins, int n) {
        if (money < 0)
            return true;
        if (coins == null)
            return true;
        if (n <= 0)
            return true;
        for (int i = 0; i < n; i++) {
            if (coins[i] <= 0)
                return true;
        }
        return false;
    }

    public static void main(String[] ar) {
        int money = 11;
        int[] coins = new int[]{2, 5, 10, 50};
        System.out.println("币值有 " + Arrays.toString(coins));

        int co = 0;

        System.out.println("\n======= 找钱 " + money);
        co = getCoinCount(money, coins, coins.length);
        System.out.println("硬币枚数: " + co);

        money = 167;
        System.out.println("\n======= 找钱 " + money);
        co = getCoinCount(money, coins, coins.length);
        System.out.println("硬币枚数: " + co);

     money = 3;
        System.out.println("\n======= 找钱 " + money);
        co = getCoinCount(money, coins, coins.length);
        System.out.println("硬币枚数: " + co);

} }

 

 

 

 

 

输出

======= 找钱 11
coinValue(面值): 5
coinValue(面值): 2
coinValue(面值): 2
coinValue(面值): 2
硬币枚数: 4

======= 找钱 167
coinValue(面值): 50
coinValue(面值): 50
coinValue(面值): 50
coinValue(面值): 10
coinValue(面值): 5
coinValue(面值): 2
硬币枚数: 6

 

======= 找钱 3
硬币枚数: -1

 

posted on 2019-12-10 03:56  jald  阅读(393)  评论(0编辑  收藏  举报

导航