目标总和未输出正确的值

时间:2019-09-06 22:19:37

标签: javascript algorithm

我正在尝试解决目标总和问题(https://leetcode.com/problems/target-sum/description/

You are given a list of non-negative integers,
a1, a2, ..., an, and a target, S. Now you have 2 symbols
+ and -. For each integer, you should choose one from
+ and - as its new symbol. Find out how many ways to assign symbols
to make sum of integers equal to target S

我能够通过基本案例,但([0,1], 1)的案例失败了。

试图了解我在做什么错以及如何解决。

var findTargetSumWays = function(nums, total) {
    const res = [];
    let str = nums.join('');
    helper(str, '', 0);

    function helper(str, curStr, sum) {
        if(str.length===0) return;
        if(sum + Number(str) === total) {
            res.push(`${curStr}+${str}`);
            return;
        }

        if(sum - Number(str) === total) {
            res.push(`${curStr}-${str}`);
            return;
        }

        for(let i=0; i<str.length; i++) {
            helper(str.substring(i+1), `${curStr}+${str.substring(0, i+1)}`, sum+Number(str.substring(0, i+1)));
            helper(str.substring(i+1), `${curStr}-${str.substring(0, i+1)}`, sum-Number(str.substring(0, i+1)))
        }
    }
    
    return res.length; 
};
console.log(findTargetSumWays([1, 1, 1, 1, 1], 3)); // Returns correct answer.
console.log(findTargetSumWays([1, 0], 1)); // Expected 2

试图找出第二个输入的问题。

我的算法如下:

  

1)将数组转换为字符串。

     

2)在数字上执行DFS,例如:“ 123456”

2 个答案:

答案 0 :(得分:1)

您对DFS的想法可能适用于足够小的输入(问题的约束可能支持),但是我对为什么将数字列表转换为字符串而不是直接对数字执行DFS感到困惑。要查找当前方法中的错误,我建议向控制台编写中间步骤,以查看它们是否符合您的期望。

这里有一些公认的代码,其思想是通过包​​含我们先前迭代中已有的方式来构建可能的总和并计算达到这些总和的方式。希望代码能使之清楚。

function f(nums, S){
  let sums = {0: 1}

  for (let i=0; i<nums.length; i++){
    let newSums = {}
    for (let [sum, ways] of Object.entries(sums)){
      if (nums[i] == 0){
        newSums[sum] = 2 * ways
        continue
      }
      added = Number(sum) + nums[i]
      subtracted = Number(sum) - nums[i]
      newSums[added] = ways + (newSums[added] || 0)
      newSums[subtracted] = ways + (newSums[subtracted] || 0)
    }
    sums = newSums
  }
  return sums[S] || 0
}

var A = [1, 0]
var target = 1
console.log(f(A, target))

A = [1, 1, 1, 1, 1]
target = 3
console.log(f(A, target))

答案 1 :(得分:0)

我认为,在您的将数组转换为字符串的算法中是多余的,因为您可以直接使用算法的第二步,即dfs / recursion,就像下面这样(抱歉,我不懂JavaScript,但我希望这样做更容易阅读)

int find_it(vector<int> & a , int i , int n , int &sum , int sum_till_now)
{
    if(i == n)
    {
        if(sum_till_now == sum)
            return 1;
        return 0;
    }
    int first = find_it(a , i + 1 , n , sum ,  sum_till_now - a[i]);
    first += find_it(a , i + 1 , n , sum , sum_till_now + a[i]);

    return first;
}
int findTargetSumWays(vector<int>& nums, int sum) {
    int n = nums.size();
    int ways = find_it(nums , 0 , n , sum , 0);
    return ways;  
} 

但是如果您考虑优化,那么您将看到recursion / dfs树正在生成许多重复的递归函数,我们如何避免重复的递归函数,如果我们知道状态是什么,那么可以使用动态编程形成了哪些不同的递归函数,这里是i(当前位置)和sum_till_now(我的意思是我们可以通过这两个参数在递归树中更改两个递归函数,所以在动态编程中是状态),所以如果我们存储状态的那些组合,这等效于保存函数信息,因此下次我们不必再次访问已访问/已保存函数的递归树。一方面,有人认为这不是动态编程,它是dfs +备忘录,但又有许多其他人认为这是自上而下的动态编程,因为递归还探讨了子问题,然后我们保存了不同的状态。

以下是优化版本:

vector<vector<int>>dp
int find_it(vector<int> & a , int i , int n , int &sum , int sum_till_now)
{
    if(i == n)
    {
        if(sum_till_now == sum)
            return 1;
        return 0;
    }
    if(dp[i][sum_till_now + 1000] != -1)
        return dp[i][sum_till_now + 1000];

    int first = find_it(a , i + 1 , n , sum ,  sum_till_now - a[i]);
    first += find_it(a , i + 1 , n , sum , sum_till_now + a[i]);

    return dp[i][sum_till_now + 1000] = first;
}
int findTargetSumWays(vector<int>& nums, int sum) {
    int n = nums.size();
    dp = vector<vector<int>> (n, vector<int>(2001 , -1));
    int ways = find_it(nums , 0 , n , sum , 0);
    return ways;   
}

一件事,我在使用dp值或将值分配给dp时在sum_till_now中添加了1000,因为给出了最大和可以达到1000,因此对称地表明,负值的最大和也只能上升到1000 -1000,因此由于数组或向量索引不能具有负索引,因此我将sum_till_now增加了1000,使其变为非负。