2

Why does the order of the iteration loop matter here?

    def change1(self, amount, coins):
        """
        :type amount: int
        :type coins: List[int]
        :rtype: int
        """
        if amount == 0:
            return 1

        dp = [0 for _ in range(amount + 1)]
        dp[0] = 1

        # dp[i] to denote the number of ways to sum up to amount i.
        for j in range(len(coins)): # switch those 2 statements would be wrong answer why???
            for i in range(1, amount + 1):
                if i - coins[j] >= 0:
                    dp[i] += dp[i - coins[j]]

        return dp[amount]

    def change2(amount, coins): # wrong answer
        """
        :type amount: int
        :type coins: List[int]
        :rtype: int
        """
        if amount == 0:
            return 1

        dp = [0 for _ in range(amount + 1)]

        ## when amount is 0, # combinations is 1
        dp[0] = 1

        # dp[i] to denote the number of ways to sum up to amount i.
        for i in range(1, amount + 1):
            for j in range(len(coins)):  # switch those 2 statements would be wrong answer why???
                if i - coins[j] >= 0:
                    dp[i] += dp[i - coins[j]]

        return dp[amount]

change 1

dp[i-1] = dp[i-1-c2] + dp[i-1-c2] + dp[i-2-c2] + ..dp[0]

dp[i] = dp[i-c1] + dp[i-1-c1] + dp[i-2-c1] + ...dp[0] This is correct solution.

change 2

dp[i-1] = dp[i-1-c1] + dp[i-1-c2] + dp[i-1-c3] + dp[i-1-c4] + ... dp[0]

dp[i] = dp[i-c1] + dp[i-c2] + dp[i-c3] + dp[i-c4] + ... dp[0]

Why this approach is WRONG?

Total amount i can be derived from a summation of choices from taking coin c1 or c2 or c3, this is more intuitive in my opinion.

I've checked a few references but couldn't find a satisfying answer.

EDIT: I also understand changing the order of nested loop will give wrong answer, I'm trying to understand why the 2nd approach has the wrong intuition while the 1st is correct.

Coin Change: number of solutions when order matters vs. when order doesn't matter

Coin change using dynamic programming

jen007
  • 1,389
  • 3
  • 15
  • 19

1 Answers1

2

Obviously it must give wrong answer when you change the order of the nested loops.

The correct dp state of the problem is defined as follows:

dp[i][j] = # of ways to form amount j using first i coins

The optimal substructure here is that once you have solution for first i coins, you can extend it for i+1 coins.

But when you flip the order of execution of loops, this substructure property does not hold.

In this case, you are first calculating the number of ways to form amount i using all the coins and extending it to find the number of ways to form amount i+1, for which the optimal substructure property does not hold true.

Raj
  • 664
  • 4
  • 16
  • I didn't use `dp[i][j]` but `dp[amount]` denoting the number of ways to sum up to amount i. By definition, it should be `dp[amount] = dp[amount-c1] + dp[amount-c2] ...` – jen007 Jan 16 '20 at 14:01
  • I used `dp[i][j]` for the sake of simplicity of explanation. If you look carefully then the `dp` state defined on `dp[n][amount]` is equivalent to `dp[amount]` where in each iteration instead of updating the original `dp` array, you have a new row for each iteration. https://stackoverflow.com/questions/27880842/space-optimized-solution-for-coin-change – Raj Jan 16 '20 at 14:06
  • Yes, that explanation makes sense if you are optimizing from dp[i][j], however, what's wrong with`dp[amount]`denotation above - number of ways summing to amount using any of the coins – jen007 Jan 16 '20 at 14:27
  • 1
    Both are same! https://stackoverflow.com/questions/27880842/space-optimized-solution-for-coin-change – Raj Jan 16 '20 at 14:32
  • Thanks, basically denotation on dp[amount] in my case is wrong, dp[amount] is really the number of ways to sum up to amount i, `using the first j coins`. – jen007 Jan 16 '20 at 23:14