Coin Change II

Coin Change – Lintcode 740

已知

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

示例

amount = 10,coins[] = { 2 , 3 , 8 }, 结果为3
8 = 8
8 = 3 + 3 + 2
8 = 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)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值