简单编程题目连载(六)——找零钱

来源:互联网 发布:java编程那些事儿 编辑:程序博客网 时间:2024/05/11 18:09

上篇中对找零钱题目进行了第二种方法的探索,这次将使用经典的动态规划方法,求解这道题目。

对于记忆搜索法来说,这基本是动态规划的雏形。就是减少重复递归的次数,但记忆搜索法是无序的记忆,遇到一个记录一个,没什么规律。经典的动态规划方法就是按照一定的次序和规律记录每一次递归的值。看下边的例子。

对于penny{1,3,6},aim=13这个条件来说,定义一个二维数组,行为N(N为penny的长度)即就是3,列为目标值加1就是14。map[2][13]数组(因为数组从0开始)的每一个map[i][j]代表的含义是:使用penny[0~i]区间的货币种类,组合成目标为j的金额,一共有的方法数。

对于每一个具体的map[i][j]求解方法为:

使用0张penny[i]货币,其余使用penny[0~i-1]货币种类时,此时的方法数为map[i-1][j]。

使用1张penny[i]货币,其余使用penny[0~i-1]货币种类时,此时的方法数为map[i-1][j-penny[i]*1]。

使用x张penny[i]货币,其余使用penny[0~i-1]货币种类时,此时的方法数为map[i-1][j-penny[i]*x]。

而map[i][j]的值为以上各值的累加。

用上边的例子做成表格帮助理解

0 1 2 3 4 5 6 7 8 9 10 11 12 13 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 3 3 3 4 4 4 5 5 2 1 1 1 2 2 2 3 4 4 6 6 6 8 9

上边的表格为map[i][j]的所有值。大家可以自己验证一下每个格与它上方一行值的关系。

比如map[2][13],求这个值就是在求map[1][13],map[1][7],map[1][1]这几个值的累加。为什么?因为map[2][13]就代表着使用面额1、3、6组成目标13的方法数,而上边说过,当使用0张6元,就意味着求使用面额1、3组成目标13的方法数即为map[1][13];使用1张6元,就意味着使用面额1、3组成目标7的方法数即为map[1][7];使用2张6元,就意味着使用面额1、3组成目标1的方法数即为map[1][1]。

以此类推,求map[1][13]时,就是在求map[0][13],map[0][10],map[0][7],map[0][4],map[0][1],而第一行代表着的意思是:只使用penny[0],组成目标aim的方法数,对于上边的例子就是只使用1块,组成1~13的目标值,显然每个目标只有一种方法。而第一列代表的意思是:使用每个面值,组成目标金额为0的方法数,这个显而易见,使用0张,即1种方法。这里要注意,组成目标金额为0也是一种需求。

到这里就能明白动态规划的意思了,是一种有条理有顺序的做法。并且在我上边的举例过程中,已经将动态规划的优化版间接说了。

当使用经典的动态规划时,需要按顺序算出每一个map值,这与记忆搜索法的时间复杂度是一样的,只是这个有顺序,而这个有顺序就给优化带来了可能。如同我上边的举例描述的过程一样,从最终的目标向前推,每次计算的格子都是有用的格子,并且最终直接返回目标结果,极大的节省了时间。下边进行动态规划优化版的代码示例:

public int countType(int[] penny,int n,int aim){    if(penny == null || n <= 0 || aim < 0){        return 0;    }    int[][] map = new int[penny.length][aim+1];    //进行第一行的初始化    for(int i = 1; aim - penny[0]*i >= 0; i++){        map[0][penny[0]*i] = 1;    }    //进行第一列的初始化    for(int i = 0; i < n; i++){        map[i][0] = 1;    }    return res(penny,penny.lenght-1,aim,map);}public int res(int[] penny,int index,int aim,int[][] map){    int result = 0;    //递归基  到达第一行或第一列  就有值返回    if(index == 0 && aim >= 0){        return map[index][aim];    }else{        for(int i = 0;aim - penny[index]*i >= 0;i++){            //只去递归对最终结果有帮助的‘格子’            result += res(penny,index-1,aim-penny[index]*i,map);        }        return result;    }}

以上为经典动态规划‘找零钱’题目的三种解法。

因为当开始接触动态规划时直接看动态规划的代码,看不懂,也不知道怎么来的,通过这三篇,知道了动态规划就是由最简单粗暴的暴力求解法一步一步优化而来,这也是动态规划题目化简的一般思路。

0 0
原创粉丝点击