使用动态规划打印目标总和的所有骰子组合
Printing all dice combinations for a target sum using dynamic programming
我有一个问题要解决,我们有 n 个骰子,每个骰子有 6 个面。我需要打印所有可能的组合,其中面朝上数字的总和等于目标。
例如:如果 n=2, target=10
,那么我需要打印 (4,6), (6,4), (5,5)
我能够使用下面的代码解决这个问题,但我想要一个使用动态编程的更优化的解决方案。
另外,我不确定如何计算下面代码的时间复杂度,请帮助我找到下面代码的时间复杂度(大 O 表示法),并帮助我使用动态优化和降低这段代码的时间复杂度编程和记忆
public class Test {
public static void main(String[] args) {
int numDie = 2;
int target = 10;
int sides = 6;
printTargetSumCombination(numDie, target, sides, new ArrayList<>());
}
private static void printTargetSumCombination(int numDie, int target, int sides, List<Integer> chosen) {
if (numDie == 0) {
if(chosen.stream().mapToInt(Integer::intValue).sum()==target)
System.out.println(chosen);
} else {
for (int i = 1; i <= sides; i++) {
chosen.add(i);
printTargetSumCombination(numDie - 1, target, sides, chosen);
chosen.remove(chosen.size() - 1);
}
}
}
}
以下是优化您的功能的选项。该方法与您的方法类似,但它包括附加条件,如果分支不能导致任何正确的解决方案,则该条件将停止递归。这显着减少了所需的迭代次数。使用您示例中的数字,迭代计数从 43 减少到 7。我不确定 O 的复杂性,它取决于输入,但通过一些测试,我看到迭代次数与解决方案的 nr 成正比,所以我假设它是 O(n)
public class Dice {
static final int target = 10;
static final int diceCount = 2;
static final int sides = 6;
static int iterations = 0;
static int validIterations = 0;
public static void main(String[] args) {
scorePrinter(sides, diceCount, new ArrayList<>());
System.out.println("Iterations nr: " + iterations);
System.out.println("Valid combinations: " + validIterations);
}
static int scorePrinter(int sides, int dice, List<Integer> scores) {
int scoresSum = scores.stream().reduce(0, Integer::sum);
if (dice == 0 && scoresSum == target) {
System.out.println(scores);
validIterations++;
return iterations++;
}
else if (dice == 0) {
return iterations++;
}
else {
for (int i = 1; i < sides + 1; i++) {
if ((dice - 1 <= (target - scoresSum - i)) && ((sides * (dice - 1)) >= target - scoresSum - i))
scorePrinter(sides, dice - 1, Stream.concat(scores.stream(), Stream.of(i)).collect(Collectors.toList()));
}
return iterations++;
}
}
}
这是一个 DP 实现。
思路是:用n
个骰子和目标t
解决问题,你可以:
- 取第一个骰子并考虑其所有可能的值
- 对于每个值
v
,考虑由 n - 1
骰子和目标 t - v
组成的子问题
- 结合
v
子问题的解
解决问题(n
、t
)后,其解决方案将存储在地图中(记忆)。
import java.util.*;
public class DiceSolver
{
private int sides;
private HashMap<String, List<int[]>> cache;
public DiceSolver(int sides)
{
this.sides = sides;
cache = new HashMap<String, List<int[]>>();
}
public List<int[]> getSolutions(int numDice, int target)
{
// No need to compute anything if the target is out of reach
if(target < numDice || target > sides * numDice)
return Collections.emptyList();
String key = numDice + "|" + target;
List<int[]> solutions = cache.get(key);
if(solutions==null)
{
// Compute the solutions and store them in the cache
solutions = computeSolutions(numDice, target);
cache.put(key, solutions);
}
return solutions;
}
private List<int[]> computeSolutions(int numDice, int target)
{
if(numDice > 1)
{
List<int[]> solutions = new ArrayList<int[]>();
for(int v=1;v<=sides;v++)
{
// Combine the first die with the solutions of the subproblem
for(int[] sol : getSolutions(numDice - 1, target - v))
solutions.add(prepend(v, sol));
}
return solutions;
}
else
{
// 1-die problem
return Collections.singletonList(new int[]{target});
}
}
private static int[] prepend(int val, int[] arr)
{
int[] res = new int[arr.length + 1];
res[0] = val;
System.arraycopy(arr, 0, res, 1, arr.length);
return res;
}
public static void main(String[] args)
{
DiceSolver solver = new DiceSolver(6);
List<int[]> solutions = solver.getSolutions(2, 10);
solutions.forEach(sol -> System.out.println(Arrays.toString(sol)));
}
}
输出:
[4, 6]
[5, 5]
[6, 4]
我有一个问题要解决,我们有 n 个骰子,每个骰子有 6 个面。我需要打印所有可能的组合,其中面朝上数字的总和等于目标。
例如:如果 n=2, target=10
,那么我需要打印 (4,6), (6,4), (5,5)
我能够使用下面的代码解决这个问题,但我想要一个使用动态编程的更优化的解决方案。 另外,我不确定如何计算下面代码的时间复杂度,请帮助我找到下面代码的时间复杂度(大 O 表示法),并帮助我使用动态优化和降低这段代码的时间复杂度编程和记忆
public class Test {
public static void main(String[] args) {
int numDie = 2;
int target = 10;
int sides = 6;
printTargetSumCombination(numDie, target, sides, new ArrayList<>());
}
private static void printTargetSumCombination(int numDie, int target, int sides, List<Integer> chosen) {
if (numDie == 0) {
if(chosen.stream().mapToInt(Integer::intValue).sum()==target)
System.out.println(chosen);
} else {
for (int i = 1; i <= sides; i++) {
chosen.add(i);
printTargetSumCombination(numDie - 1, target, sides, chosen);
chosen.remove(chosen.size() - 1);
}
}
}
}
以下是优化您的功能的选项。该方法与您的方法类似,但它包括附加条件,如果分支不能导致任何正确的解决方案,则该条件将停止递归。这显着减少了所需的迭代次数。使用您示例中的数字,迭代计数从 43 减少到 7。我不确定 O 的复杂性,它取决于输入,但通过一些测试,我看到迭代次数与解决方案的 nr 成正比,所以我假设它是 O(n)
public class Dice {
static final int target = 10;
static final int diceCount = 2;
static final int sides = 6;
static int iterations = 0;
static int validIterations = 0;
public static void main(String[] args) {
scorePrinter(sides, diceCount, new ArrayList<>());
System.out.println("Iterations nr: " + iterations);
System.out.println("Valid combinations: " + validIterations);
}
static int scorePrinter(int sides, int dice, List<Integer> scores) {
int scoresSum = scores.stream().reduce(0, Integer::sum);
if (dice == 0 && scoresSum == target) {
System.out.println(scores);
validIterations++;
return iterations++;
}
else if (dice == 0) {
return iterations++;
}
else {
for (int i = 1; i < sides + 1; i++) {
if ((dice - 1 <= (target - scoresSum - i)) && ((sides * (dice - 1)) >= target - scoresSum - i))
scorePrinter(sides, dice - 1, Stream.concat(scores.stream(), Stream.of(i)).collect(Collectors.toList()));
}
return iterations++;
}
}
}
这是一个 DP 实现。
思路是:用n
个骰子和目标t
解决问题,你可以:
- 取第一个骰子并考虑其所有可能的值
- 对于每个值
v
,考虑由n - 1
骰子和目标t - v
组成的子问题
- 结合
v
子问题的解
解决问题(n
、t
)后,其解决方案将存储在地图中(记忆)。
import java.util.*;
public class DiceSolver
{
private int sides;
private HashMap<String, List<int[]>> cache;
public DiceSolver(int sides)
{
this.sides = sides;
cache = new HashMap<String, List<int[]>>();
}
public List<int[]> getSolutions(int numDice, int target)
{
// No need to compute anything if the target is out of reach
if(target < numDice || target > sides * numDice)
return Collections.emptyList();
String key = numDice + "|" + target;
List<int[]> solutions = cache.get(key);
if(solutions==null)
{
// Compute the solutions and store them in the cache
solutions = computeSolutions(numDice, target);
cache.put(key, solutions);
}
return solutions;
}
private List<int[]> computeSolutions(int numDice, int target)
{
if(numDice > 1)
{
List<int[]> solutions = new ArrayList<int[]>();
for(int v=1;v<=sides;v++)
{
// Combine the first die with the solutions of the subproblem
for(int[] sol : getSolutions(numDice - 1, target - v))
solutions.add(prepend(v, sol));
}
return solutions;
}
else
{
// 1-die problem
return Collections.singletonList(new int[]{target});
}
}
private static int[] prepend(int val, int[] arr)
{
int[] res = new int[arr.length + 1];
res[0] = val;
System.arraycopy(arr, 0, res, 1, arr.length);
return res;
}
public static void main(String[] args)
{
DiceSolver solver = new DiceSolver(6);
List<int[]> solutions = solver.getSolutions(2, 10);
solutions.forEach(sol -> System.out.println(Arrays.toString(sol)));
}
}
输出:
[4, 6]
[5, 5]
[6, 4]