Algorithm 动态问题中的重叠子问题(换硬币问题)

Algorithm 动态问题中的重叠子问题(换硬币问题),algorithm,recursion,logic,dynamic-programming,backtracking,Algorithm,Recursion,Logic,Dynamic Programming,Backtracking,我试图培养动态规划问题的良好直觉,但我无法理解问题的特定方面 我将以leetcode上提供的硬币兑换问题为例 在许多教程中,都提到了自底向上的方法,例如在本教程中- 在这种方法中,我们从最优解开始,然后向我们的解建立阵列。我们找到了求和2,然后求和3的最优解,依此类推。最后,我们将有我们的解决方案。这是我理解的方法 我很难用另一种带记忆的递归方法来思考问题。我已经写了一个回溯方法来解决这个问题,但不知道如何将回溯应用到这个问题上 public int changeCoins_BT(int[] c

我试图培养动态规划问题的良好直觉,但我无法理解问题的特定方面

我将以leetcode上提供的硬币兑换问题为例

在许多教程中,都提到了自底向上的方法,例如在本教程中-

在这种方法中,我们从最优解开始,然后向我们的解建立阵列。我们找到了求和2,然后求和3的最优解,依此类推。最后,我们将有我们的解决方案。这是我理解的方法

我很难用另一种带记忆的递归方法来思考问题。我已经写了一个回溯方法来解决这个问题,但不知道如何将回溯应用到这个问题上

public int changeCoins_BT(int[] coins, int target, int min, int num_curr) {

    if(target == 0) {
        return min < num_curr ? min : num_curr;
    } else if(target < 0) return min;
    else if(num_curr > min) return min;

    for(int i=0;i<coins.length;i++) {
        min = changeCoins_BT(coins,target-coins[i],min,num_curr+1);
    }

    return min;
}
public int changeconoins\u BT(int[]硬币,int目标,int最小值,int num\u curr){
如果(目标==0){
返回minmin)返回min;

对于(int i=0;i,在找到DP的递归解之前,请尝试识别所讨论问题的子问题。因为,每个子问题与父问题相同,并且将应用相同的算法

让我们以硬币兑换为例,这里给出了面额列表d[]和sum,s,我们需要找到最小面额数,count(面额数)以求和到s。如果我们想定义一个解决方案(方法)来计算count,那么 int findmindeom(int[]d,int S)。此时我们不知道它将是什么实现,但我们知道问题需要哪些参数,即d和S

请记住,子问题也会有相同的解决方案,但总和较低。因此,我们尝试以FindIndenom解决每个子问题的方式来实现。这将导致我们使用递归解决方案,其中我们使用较低的总和s调用相同的方法

int findMinDenom(int[] d, int S) {
  if (S == 0) {
    // If Sum is zero, then no denomination is required.
    return 0;
  }
  int result = Integer.MAX_VALUE; // Define Integer max
  for ( int i = 0; i < d.length; i++) {
    int s = S - d[i] // Reduced to a sub-problem
    // Handle case where s < 0
    if (s < 0) {
      continue;
    }
    int r = findMinDenom(d, s); // Solution for lower sum, s
    result = Math.min(result, r + 1); // Plus 1 because we have just used one denomination.
  }
  return result;
}
int findMinDenom(int[]d,int S){
如果(S==0){
//如果总和为零,则不需要面额。
返回0;
}
int result=Integer.MAX\u VALUE;//定义整数MAX
对于(int i=0;i
我们刚刚使用DP解决了问题。但是,没有记忆。我们将介绍使用数组来保存结果。因为我们不知道子问题是否已经解决。如果是,则只返回该子问题的结果。对于和0,面额将为零。有解[0]=0,我们希望找到问题S的解决方案,在编码术语中,解决方案[S]。因此,解决方案数组的大小为S+1

// Initialize solution
int[] solution = new int[S+1];
solution[0] = 0; 
for (int i = 1; i <= S; i++)
  solution[i] = -1; // Just to denote that solution is not found yet.
//初始化解决方案
int[]解决方案=新的int[S+1];
溶液[0]=0;
对于(int i=1;i-1){
//解决方案存在
返回溶液;
}
int result=Integer.MAX\u VALUE;//定义整数MAX
对于(int i=0;i
这通常非常简单:记住具有可更改参数的函数调用。在这种情况下,您需要mem[target][min][num\u curr].因此,在返回min之前,您存储了值,并且在函数的开头,如果您已经存储了值,那么您只需返回它,感谢响应。我们是否按照经验法则使用可以更改的参数进行记忆?如果您有足够的ram,则最好防止重新计算是的
int findMinDenomMemo(int[] d, int S, int[] solution) {
  if (solution[S] > -1) {
    // Solution exists
    return solution[S];
  }
  int result = Integer.MAX_VALUE; // Define Integer max
  for ( int i = 0; i < d.length; i++) {
    int s = S - d[i] // Reduced to a sub-problem
    // Handle case where s < 0
    if (s < 0) {
      continue;
    }
    int r = findMinDenomMemo(d, s, solution); // Solution for lower sum, s
    result = Math.min(result, r + 1); // Plus 1 because we have just used one denomination.
  }
  // Just now solved for sub problem S. So store it.
  solution[S] = result;
  return result;
}