DP7 两道换硬币的问题 Coin Change @geeksforgeeks

1 Given a value N, if we want to make change for N cents, and we have infinite supply of each of S = { S1, S2, .. , Sm} valued coins, how many ways can we make the change? The order of coins doesn’t matter.

两种思路:

1)http://www.cs.ucf.edu/~dmarino/ucf/cop3530/lectures/COP3530DynProg02.doc

特别注意到换完的硬币是没有顺序的,所以要impose order


2)http://www.geeksforgeeks.org/dynamic-programming-set-7-coin-change/

http://www.cnblogs.com/python27/p/3303721.html

http://www.mathblog.dk/project-euler-31-combinations-english-currency-denominations/


考虑是否取最后一个硬币,转换为背包问题

package DP;

import java.util.Arrays;

/**
Given a value N, if we want to make change for N cents, 
and we have infinite supply of each of S = { S1, S2, .. , Sm} valued coins, 
how many ways can we make the change? The order of coins doesn’t matter.

 */
public class CoinChange1 {

	public static int MEM[] = new int[10001];   // Can support up to 10000 peso value
     
    public static void main(String[] args) {
    	
    	int coins[] = {1, 2, 3};  // Available coin denominations
    	Arrays.sort(coins);
    	int coinKinds = coins.length;
    	int sum = 4;
    	System.out.println(countRec_1st(coins, coinKinds, sum, coins[coins.length-1]));
    	System.out.println(countRec_2nd(coins, coinKinds, sum));
    	System.out.println(countDP_1st(coins, coinKinds, sum));
    	System.out.println(countDP2D_2nd(coins, coinKinds, sum));
    	System.out.println(countDP1D_2nd(coins, coinKinds, sum));
    }
    
  //====================================第一种思路
    /*
     max 用来保持order,否则就会有重复,如(1,2),(2,1)
     */
    public static int countRec_1st(int coins[], int coinKinds, int sum, int max){
    	if(sum < 0){
    		return 0;
    	}
    	if(sum == 0){
    		return 1;
    	}
    	
    	int ways = 0;
    	for(int i=0; i<coinKinds; i++){
    		if(max >= coins[i]){
    			ways += countRec_1st(coins, coinKinds, sum-coins[i], coins[i]);
    		}
    	}
    	return ways;
    }
    
    
    // http://www.cnblogs.com/python27/p/3303721.html
    // dp[i][j] = sum(dp[i-1][j-k*coins[i-1]]) for k = 1,2,..., j/coins[i-1]
    // dp[0][j] = 1 for j = 0, 1, 2, ..., sum
    public static int countDP_1st(int[] coins, int coinKinds, int sum){
    	int[][] dp = new int[coinKinds+1][sum+1];
    	
    	for(int i=0; i<=coinKinds; i++){
    		for(int j=0; j<=sum; j++){
    			dp[i][j] = 0;
    		}
    	}
    	for(int i=0; i<=coinKinds; i++){
    		dp[i][0] = 1;
    	}
    	
    	for(int i=1; i<=coinKinds; i++){	// 币的面值
    		for(int j=1; j<=sum; j++){		// 要凑成的数量
    			dp[i][j] = 0;
    			for(int k=0; k<=j/coins[i-1]; k++){
    				dp[i][j] += dp[i-1][j-k*coins[i-1]];
    			}
    		}
    	}
    	
    	return dp[coinKinds][sum];
    }


    //====================================第二种思路
    
    // Return the count of ways we can sum coins[0...m-1] coins to get sum 
    public static int countRec_2nd(int coins[], int coinKinds, int sum){
    	if(sum == 0){	// If n is less than 0 then no solution exists
    		return 1;
    	}
    	if(sum < 0){	// If n is less than 0 then no solution exists
    		return 0;
    	}
    	if(coinKinds<=0 && sum>=1){	// If there are no coins and n is greater than 0, then no solution exist
    		return 0;
    	}
    	// count is sum of solutions (i) including coins[m-1] (ii) excluding coins[m-1]
    	// 两种情况:
    	// 1. 不使用最后一个硬币,因此实际上就是用少了最后一个硬币的coins来凑sum
    	// 2.使用最后一个硬币,因此sum的总数减少,因为每个硬币都有无数个,所以m不变
    	return countRec_2nd(coins, coinKinds-1, sum) + countRec_2nd(coins, coinKinds, sum-coins[coinKinds-1]);
    }
    
    
    public static int countDP2D_2nd(int[] coins, int coinKinds, int sum){
    	// We need n+1 rows as the table is constructed in bottom up manner using 
        // the base case 0 value case (n = 0)
    	int[][] dp = new int[sum+1][coinKinds];
    	
    	// Fill the entries for 0 value case (sum = 0)
    	for(int i=0; i<coinKinds; i++){
    		dp[0][i] = 1;
    	}
    	
    	// Fill rest of the table entries in bottom up manner  
    	for(int i=1; i<=sum; i++){		// sum
    		for(int j=0; j<coinKinds; j++){		// coins m
    			// Count of solutions including S[j]
    			int x = (i-coins[j]>=0) ? dp[i-coins[j]][j] : 0;	// 要满足i-coins[j]>=0
    			// Count of solutions excluding S[j]
    			int y = (j>=1) ? dp[i][j-1] : 0;	// 要满足j-1>=0
    			dp[i][j] = x + y;
    		}
    	}
    	return dp[sum][coinKinds-1];
    }
    
    
    public static int countDP1D_2nd(int[] coins, int coinKinds, int sum){
    	
    	// dp[i] will be storing the number of solutions for
        // value i. We need n+1 rows as the table is constructed
        // in bottom up manner using the base case (n = 0)
    	int[] dp = new int[sum+1];
    	
    	dp[0] = 1;	// Base case (If given value is 0)
    	// Pick all coins one by one and update the table[] values
        // after the index greater than or equal to the value of the
        // picked coin
    	for(int i=0; i<coinKinds; i++){			// coins m
    		for(int j=coins[i]; j<=sum; j++){	// sum
    			dp[j] += dp[j-coins[i]];
    		}
    	}
    	return dp[sum];
    }
    
}

2 What is the smallest number of coins to change? 如果换,最少会有多少枚硬币?

http://www.columbia.edu/~cs2035/courses/csor4231.F11/dynamic.pdf

C[p] = min{C[p-di]+1} 


package DP;

import java.util.Arrays;

/**
A country has coins with denominations
1 = d1 < d2 < · · · < dk.
  
You want to make change for n cents, using the smallest number
 
 */
public class CoinChange2 {

	public static int MEM[] = new int[10001];   // Can support up to 10000 peso value
    public static int coins[] = {1, 2, 3};  // Available coin denominations
     
    public static void main(String[] args) {
    	int n = 4;
        System.out.println(minCoins(n));
        System.out.println(minCoins_DP(n));
    }
    
    
    // 记忆化搜索,top-down 递归
    public static int minCoins(int n) {
        if(n < 0)
            return Integer.MAX_VALUE -1;
        else if(n == 0)
            return 0;
        else if(MEM[n] != 0)    // If solved previously already
            return MEM[n];
        else {
            // Look for the minimal among the different denominations
            MEM[n] = 1+minCoins(n-coins[0]);
            for(int i = 1; i < coins.length; i++)
                MEM[n] = Math.min(MEM[n], 1+minCoins(n-coins[i]));
            return MEM[n];
        }
    }

    // bottom-up DP
	public static int minCoins_DP(int n){
		int[] minCoins = new int[n+1];
		Arrays.fill(minCoins, Integer.MAX_VALUE);
		
		// 第一个硬币
		minCoins[0] = 0;
		
		// 算出n前的每一个可换硬币的数量
		for(int i=1; i<=n; i++){
			// 根据递推公式,看看硬币可拆分的可能性
			for(int j=0; j<coins.length; j++){
				if(coins[j] <= i){
					minCoins[i] = Math.min(minCoins[i], 1+minCoins[i-coins[j]]);
				}
			}
		}
		
		return minCoins[n];
	}
	
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值