DP30 求骰子能凑成给定和的组合数 Dice Throw @geeksforgeeks

Given n dice each with m faces, numbered from 1 to m, find the number of ways to get sum X. X is the summation of values on each face when all the dice are thrown.

TheNaive approachis to find all the possible combinations of values from n dice and keep on counting the results that sum to X.

This problem can be efficiently solved usingDynamic Programming (DP).

Let the function to find X from n dice is: Sum(m, n, X)
The function can be represented as:
Sum(m, n, X) = Finding Sum (X - 1) from (n - 1) dice plus 1 from nth dice
               + Finding Sum (X - 2) from (n - 1) dice plus 2 from nth dice
               + Finding Sum (X - 3) from (n - 1) dice plus 3 from nth dice
                  ...................................................
                  ...................................................
                  ...................................................
              + Finding Sum (X - m) from (n - 1) dice plus m from nth dice

So we can recursively write Sum(m, n, x) as following
Sum(m, n, X) = Sum(m, n - 1, X - 1) + 
               Sum(m, n - 1, X - 2) +
               .................... + 
               Sum(m, n - 1, X - m)

Why DP approach?
The above problem exhibits overlapping subproblems. See the below diagram. Also, seethisrecursive implementation. Let there be 3 dice, each with 6 faces and we need to find the number of ways to get sum 8:

diceThrow2

Sum(6, 3, 8) = Sum(6, 2, 7) + Sum(6, 2, 6) + Sum(6, 2, 5) + 
               Sum(6, 2, 4) + Sum(6, 2, 3) + Sum(6, 2, 2)

To evaluate Sum(6, 3, 8), we need to evaluate Sum(6, 2, 7) which can 
recursively written as following:
Sum(6, 2, 7) = Sum(6, 1, 6) + Sum(6, 1, 5) + Sum(6, 1, 4) + 
               Sum(6, 1, 3) + Sum(6, 1, 2) + Sum(6, 1, 1)

We also need to evaluate Sum(6, 2, 6) which can recursively written
as following:
Sum(6, 2, 6) = Sum(6, 1, 5) + Sum(6, 1, 4) + Sum(6, 1, 3) +
               Sum(6, 1, 2) + Sum(6, 1, 1)
..............................................
..............................................
Sum(6, 2, 2) = Sum(6, 1, 1)

Please take a closer look at the above recursion. The sub-problems inREDare solved first time and sub-problems inBLUEare solved again (exhibit overlapping sub-problems). Hence, storing the results of the solved sub-problems saves time.


For DP:

Time Complexity:O(m * n * x) where m is number of faces, n is number of dice and x is given sum.

We can add following two conditions at the beginning of findWays() to improve performance of program for extreme cases (x is too high or x is too low)

// When x is so high that sum can not go beyond x even when we
// get maximum value in every dice throw.
if (m*n <= x)
return (m*n == x);
// When x is too low
if (n >= x)
return (n == x);

With above conditions added, time complexity becomes O(1) when x >= m*n or when x <= n.

文中的骰子不一定就是6面的,可以是任意面。


package DP;

public class DiceThrow {

	public static void main(String[] args) {
		System.out.println(findWaysRec(4, 2, 1));
		System.out.println(findWaysRec(2, 2, 3));
		System.out.println(findWaysRec(6, 3, 8));
		System.out.println(findWaysRec(4, 2, 5));
		System.out.println(findWaysRec(4, 3, 5));
		System.out.println(findWaysDP(4, 2, 1));
		System.out.println(findWaysDP(2, 2, 3));
		System.out.println(findWaysDP(6, 3, 8));
		System.out.println(findWaysDP(4, 2, 5));
		System.out.println(findWaysDP(4, 3, 5));
	}

	// 返回能使组合之和为sum的方法数
	// faces:一个dice的最大面值,所有面值为:[1...faces]
	// dices:dices的个数,每个dice都可以选择一个face
	public static int findWaysRec(int faces, int dices, int sum){
		if(sum < 1){		// 总和过小
			return 0;
		}
		if(dices == 1){	// 只有一个dice,判断总和和最大面值哪个大
			return sum<=faces ? 1 : 0;
		}
		
		int ways = 0;
		for(int i=1; i<=faces; i++){
			ways += findWaysRec(faces, dices-1, sum-i);
		}
		return ways;
	}
	
	// The main function that returns number of ways to get sum 
	// Time Complexity: O(m * n * x) where m is number of faces, n is number of dice and x is given sum.
	public static int findWaysDP(int faces, int dices, int sum){
		int[][] dp = new int[dices+1][sum+1];	//dp[dices][sum]
		
		for(int sum_=1; sum_<=sum; sum_++){	// 只有1个dice且sum大于
			if(sum_ <= faces){
				dp[1][sum_] = 1;
			}
		}
		
		for(int dices_=2; dices_<=dices; dices_++){		// dices
			for(int sum_=1; sum_<=sum; sum_++){			// sum
				for(int faces_=1; faces_<=faces; faces_++){
					if(sum_-faces_ >= 0){		// 防越界
						dp[dices_][sum_] += dp[dices_-1][sum_-faces_];
					}
				}
			}
		}
		
		return dp[dices][sum];
	}
}


http://www.geeksforgeeks.org/dice-throw-problem/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值