硬币表示_java_动态规划

硬币表示

  • 问题描述
  • 问题分析
  • 代码实现
    • 1.递推法
    • 2.递归法
    • 3.暴力破解法

问题描述

假设我们有8种不同面值的硬币{1,2,5,10,20,50,100,200},用这些硬币组合够成一个给定的数值n。 例如n=200,那么一种可能的组合方式为 200 = 3 * 1 + 1*2 + 1*5 + 2*20 + 1 * 50 + 1 * 100. 问总共有多少种可能的组合方式?

问题分析

假如只有面值1的硬币,那么组成任何数值都只有1种组合方法。

假如有1和2两种面值得硬币,那么首先可以分为两种情况,一个是使用面值2的硬币,一个是不使用面值2的硬币,使用面值为2的硬币又可细分为使用几枚面值为2的硬币。

假如总值为5,即可分为使用0枚面值2的硬币,使用1枚面值2的硬币,使用2枚面值2的硬币,这样便将问题分为3个子问题,用面值1的硬币组合总数值为(5 - 0 * 2) = 5,(5 - 1 * 2) = 3,(5 - 2 * 2) = 1,由上述可知这三种情况的组合方法已经得出,总数值为5的用1和2两枚硬币的组合方法有1+1+1 = 3种。
如果再加上面值为5的的硬币,则可以细分成使用0枚面值5的硬币,使用1枚面值5的硬币,使用0枚面值5的硬币的组成方法前面已经求出为3种,使用1枚面值5的硬币只有1种组成方法,因此总方法为3+1=4种。

由上诉可以得出,每一个数值都可像这样细分成多个子问题求解,下图即是每一个数值在不同面值硬币下的方法递推图,从小到大一步一步得出结果,。每一个数值所分成的子问题都是在前面已经得出的结果,只需相加即可
硬币表示_java_动态规划_第1张图片

代码实现

1.递推法

import java.util.Scanner;

public class CoinCombination {
	static int[] money = {1, 2, 5, 10, 20, 50, 100, 200};
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		System.out.println(combine(n));
	}
	private static int combine(int n) {
		//使用二维数组缓存递推过程
		//行表示面值情况,列表示每一种数值的组成方法
		int[][] dp = new int[8][n + 1];
		//初始化,任何种面值组成总数值为0都只有1种组成方法
		for (int i = 0; i < 8; i++) {
			dp[i][0] = 1;
		}
		//初始化,只有面值1的任何数值都只有1种组成方法
		for (int i = 0; i < n + 1; i++) {
			dp[0][i] = 1;
		}
		//计算每一个数值在每一种面值情况下的组成方法数
		for (int i = 1; i < 8; i++) {
			for (int j = 1; j < n + 1; j++) {
				//循环遍历使用了k枚该面值硬币后的组成方法数
				for (int k = 0; k * money[i] <= j; k++) {
					//将问题拆成子问题再相加
					dp[i][j] += dp[i - 1][j - k * money[i]];
				}
			}
		}
		//返回所需要的数值组成方法
		return dp[7][n];
	}
}

2.递归法

该方法利用了上述思维,但是代码更简洁

import java.util.Scanner;

public class CoinCombination {
	static int[] money = {1, 2, 5, 10, 20, 50, 100, 200};
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		System.out.println(combine(n,7));
	}
	private static int combine(int n,int k) {
		if (n < 0) {
			return 0;
		}
		if (n == 0) {
			return 1;
		}
		int cut = 0;
		//循环遍历使用了k枚该面值硬币后的组成方法数再相加
		for (int i = 0; k >= 0 && i * money[k] <= n; i++) {
		//每一层递归都在拆分成更小的问题,直到只有面值1的硬币
			cut += combine1(n - i * money[k], k - 1);
		}
		return cut;
	}
}

3.暴力破解法

当然,最容易想到的自然是暴力破解,但是时间复杂度较高,不建议使用

import java.util.Scanner;

public class CoinCombination {
	static int[] money = {1, 2, 5, 10, 20, 50, 100, 200};
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		System.out.println(combine(n));
	}
	private static int combine(int n) {
		int cut = 0;
		for (int i = 0; i <= n; i++) {
			for (int j = 0; j <= n / 2; j++) {
				for (int k = 0; k <= n / 5; k++) {
					for (int l = 0; l <= n / 10; l++) {
						for (int m = 0; m <= n / 20; m++) {
							for (int o = 0; o <= n / 50; o++) {
								for (int p = 0; p <= n / 100; p++) {
									for (int q = 0; q <= n / 200; q++) {
										if (1 * i + 2 * j + 5 * k + 10 * l + 20 * m + 50 * o + 100 * p + 200 * q == n) {
											cut ++;
										}
									}
								}
							}
						}
					}
				}
			}
		}
		return cut;
	}
}

你可能感兴趣的:(蓝桥杯)