Coin Change II

来源:互联网 发布:淘宝旺旺聊天刷单禁词 编辑:程序博客网 时间:2024/06/05 06:23

Coin Change – Lintcode 740

已知

给我们一个总额amount和一个字符串,字符串内的数字可以循环使用,求让数字总和为amount的数字串的总个数

示例

amount = 10,coins[] = { 2 , 3 , 8 }, 结果为38 = 88 = 3 + 3 + 28 = 2 + 2 + 2 + 2

思路

这是一道典型的动态规划问题,和普通自由的动态规划问题相比有一个限制条件,用示例举例,3,3,2只能算一种组合方式,而正常情况按照排列算三种。如何**限制重复的排列方式**呢?一种较为简单的方法就是**限制一下当前能取的最大值**。举个栗子,当示例的8先取2,剩余为6时,我们能取的最大数字就变成了2,也就是再也无法取3和8了。

根据以上思路,动态规划的递归式代码如下:

//index代表排序后的coins数组中,当前 限制的最大值 的下标private int calculate(int amount, int[] coins, int index) {        if (amount < 0) {            return 0;        }        if (amount == 0) {            return 1;        }        if (index == 0) {            return amount % coins[0] == 0 ? 1 : 0;        }        int ret = 0;        for (int i = index; i >= 0; i--) {            ret += calculate(amount - coins[i], coins, i);        }        return ret;    }

以上代码代表的是基本思路,当然要通过案例也不是不可能 (如果测试用例都很简单的话),不过确实没有通过,当数字到了一定规模,因为递归的重复调用,时间复杂度会比较高。这个时候我们只能从下往上构筑,依次递推出当前值。

递推的思路如下:

①设置temp数组的行数为amount的值,列数为coins数组的长度(也就是说,temp[i][j]代表使用 **当前数组直到下标为j的子数组****获得值为i** 的组合的个数)( 在这里为了方便理解,行数设置为amount + 1,行的下标为多少,就代表当前值为多少 )②首先初始化直到列数为1时的所有行的值,用以递推后面的数据,以示例为例子,coins[0] = 2, 所以(1,0)(3,0)(5,0),(7,0)值为0,(2,0)(4,0)(6,0),(8,0)值为1③进行递推,(i , j)的值来源有三种:第一种是(i,j-1), 也就是当前coins[j]取不到,第二种是只取coins[j] (答案只能为1或0种),第三类是取一次coins[j]到能取coins[j]的次数最大值 ,取X次的结果应该是(i - coins[j] * X, j - 1)的值。

综上,优化后的代码如下:

static int[][] count;    public int change(int amount, int[] coins) {        //不排序很难确保唯一        Arrays.sort(coins);        init(amount, coins);        return count[amount][coins.length - 1];    }    private void init(int amount, int[] coins) {        count = new int[amount + 1][coins.length];        for (int j = 1; j < count.length; j++) {            if (j % coins[0] == 0) {                count[j][0] = 1;            }        }        for (int i = 1; i < coins.length; i++) {            for (int j = 1; j < count.length; j++) {                if (j % coins[i] == 0) {                    //来源2                    count[j][i] = 1;                }                //来源1                count[j][i] += count[j][i - 1];                //来源3                for (int k = j - coins[i]; k > 0; k -= coins[i]) {                    count[j][i] += count[k][i - 1];                }            }        }    }

以上代码快速通过了~

谢谢阅读,希望对您有所帮助(T▽T)