【问题标题】:Dynamic Programming (Codility Q: NumberSolitaire)动态规划(Codility Q:NumberSolitaire)
【发布时间】:2016-03-10 02:17:39
【问题描述】:

这是一个问题: codility.com/programmers/task/number_solitaire

下面的链接是我的结果(50% 来自 Codility): https://codility.com/demo/results/training8AMJZH-RTA/

我的代码(起初,我尝试使用 Kadane 的算法解决这个问题):

class Solution {
    public int solution(int[] A) {
        int temp_max = Integer.MIN_VALUE;
        int max = 0;
        int k = 1;

        if(A.length == 2) return A[0] + A[A.length-1];

        for(int i = 1; i < A.length-1; i++) {
            if(temp_max < A[i]) temp_max = A[i];

            if(A[i] > 0) {
                max += A[i];
                temp_max = Integer.MIN_VALUE;
                k = 0;           

            } else if(k % 6 == 0) {
                max += temp_max;
                temp_max = Integer.MIN_VALUE;
                k = 0;
            }
            k++;
        }
        return A[0] + max + A[A.length-1];
    }

以下是我从网上找到的解决方案(100% 来自 Codility 结果):

class Solution {
    public int solution(int[] A) {
        int[] store = new int[A.length];
        store[0] = A[0];
        for (int i = 1; i < A.length; i++) {
            store[i] = store[i-1];
            for (int minus = 2; minus <= 6; minus++) {
                if (i >= minus) {
                    store[i] = Math.max(store[i], store[i - minus]);
                } else {
                    break;
                }
            }
            store[i] += A[i];
        }
        return store[A.length - 1];
    }
}

我不知道我的代码有什么问题:(

我尝试了几个测试用例,但解决方案和我的代码没有什么不同

但是,代码测试结果显示我的并不完全正确。 (https://codility.com/demo/results/training8AMJZH-RTA/)

请任何人解释我的代码的问题~~

【问题讨论】:

    标签: java dynamic-programming


    【解决方案1】:

    试试这个测试用例[-1, -2, -3, -4, -3, -8, -5, -2, -3, -4, -5, -6, -1]。 您的解决方案返回 -4 (A[0],A[1],A[length-1],这是错误的),但正确答案是 -6 (A[0],A[6],A[length -1])。

    这是我的解决方案,很容易理解:

    public int solution(int[] A) {
        int lens = A.length;
        int dp[] = new int[6];
        for (int i = 0; i < 6; i++) {
            dp[i] = A[0];
        }
        for (int i = 1; i < lens; i++) {
            dp[i%6] = getMax6(dp) + A[i];
        }
        return dp[(lens-1)%6];
    }
    
    private int getMax6(int dp[]){
        int max = dp[0];
        for (int i = 1; i < dp.length; i++) {
            max = Math.max(max, dp[i]);
        }
        return max;
    }
    

    【讨论】:

    • 一开始并不那么容易理解,但确实很好的解决方案。
    • @kolakao (or OP) 你能解释一下为什么在这一行中添加 A[i]:dp[i%6] = getMax6(dp) + A[i];以及解决方法的返回声明
    • @user146303 在这一行 dp[i%6] = getMax6(dp) + A[i];您计算从最后 6 个字段跳转到当前字段(A[i])的最高分数。不知道你的return语句是什么意思,你能重新提出这个问题吗?
    • (A[0],A[6],A[length-1]) 是 (-1 -5 -1) 应该是 -7 而不是 6。
    【解决方案2】:

    因为您没有使用动态规划,所以您使用的是贪心算法。当一个范围内的最大数字不是正确的选择时,您的代码将失败。

    function solution(A) {
      // This array contains a maximal value at any index.
      const maxTill = [A[0]];
    
      // It's a dynamic programming so we will choose maximal value at each
      // Index untill we reach last index (goal)
      for (let i = 1; i < A.length; i++) {
        // Step 1 . max value of each index will be atleast equal to or greater than
        // max value of last index.
        maxTill[i] = maxTill[i - 1];
        // For each index we are finding the max of last 6 array value
        // And storing it into Max value.
        for (let dice = 1; dice <= 6; dice++) {
          // If array index is itself less than backtrack index
          // break as you dont have 6 boxes in your left
          if (dice > i) {
            break;
          } else {
            // The most important line .
            // Basically checking the max of last 6 boxes using a loop.
            maxTill[i] = Math.max(
              maxTill[i - dice],
              maxTill[i]
            );
          }
        }
        // Until this point maxStill contains the maximal value
        // to reach to that index.
        // To end the game we need to touch that index as well, so
        // add the value of the index as well.
        maxTill[i] += A[i];
      }
      return maxTill[A.length - 1];
    }
    
    console.log(solution([-1, -2, -3, -4, -3, -8, -5, -2, -3, -4, -5, -6, -1]));

    【讨论】:

      【解决方案3】:

      这是我的解决方案。我试图使代码易于理解。它可能无法尽可能节省空间。

          private static int solution(int A[])
      {   
          // N //  N is an integer within the range [2..100,000];
          // A[] // each element of array A is an integer within the range [−10,000..10,000].
          int N = A.length;
          int[] bestResult = new int[N]; // record the current bestResult
          Arrays.fill(bestResult, Integer.MIN_VALUE); // fill in with the smallest integer value
      
          // initialize
          bestResult[0] = A[0];
          for (int i = 0;i < A.length;i++) {
              // calculate six possible results every round
              for (int j = i + 1; (j < A.length) && (i < A.length) && j < (i + 1) + 6; j++) {
                  // compare
                  int preMaxResult = bestResult[j]; // the max number so far
                  int nowMaxResult = bestResult[i] + A[j]; // the max number at bestResult[i] + A[j]
                  bestResult[j] = Math.max(preMaxResult, nowMaxResult);
              }
          }        
          return bestResult[bestResult.length-1];
      }
      

      【讨论】:

        【解决方案4】:

        这是简单的 Python 3 解决方案:

        import sys
        
        def solution(A):        
            dp = [0] * len(A)
            dp[0] = A[0]
        
            for i in range(1, len(A)):
                maxVal = -sys.maxsize - 1
        
                for k in range(1, 7):
                    if i-k >= 0:
                        maxVal = max(maxVal, dp[i-k] + A[i])
        
                dp[i] = maxVal
            return dp[len(A)-1]
        

        【讨论】:

          【解决方案5】:

          根据发布的解决方案,我编写了可读性很好的代码。不是最佳性能。

          int[] mark = new int[A.length];
              mark[0] = A[0];
              IntStream.range(1, A.length)
                      .forEach(i -> {
                          int max = Integer.MIN_VALUE;
                          mark[i] = IntStream.rangeClosed(1, 6)
                                  .filter(die -> i - die >= 0)
                                  .map(r -> Math.max(mark[i - r] + A[i], max))
                                  .max().orElse(max);
                      });
          
              return mark[A.length - 1];
          

          【讨论】:

            【解决方案6】:

            Java 的可读解决方案:

            public class Solution {
                public static void main(String[] args) {
                    System.out.println(new Solution().solution(new int[]{1, -2, 0, 9, -1, -2}));
                }
            
                private int solution(int[] A) {
                    int N = A.length;
                    int[] dp = new int[N];
                    dp[0] = A[0];
            
                    for (int i = 1; i < N; i++) {
                        double sm = Double.NEGATIVE_INFINITY;
                        for (int j = 1; j <= 6; j++) {
                            if (i - j < 0) {
                                break;
                            }
                            double s1 = dp[i - j] + A[i];
                            sm = Double.max(s1, sm);
                        }
                        dp[i] = (int) sm;
                    }
            
                    return dp[N-1];
                }
            }
            

            【讨论】:

              【解决方案7】:

              这是一个类似于@0xAliHn 但使用更少内存的解决方案。你只需要记住最后的 6 个动作。

              def NumberSolitaire(A):
                  dp = [0] * 6
                  dp[-1] = A[0]
              
                  for i in range(1, len(A)):
                      maxVal = -100001
              
                      for k in range(1, 7):
                          if i-k >= 0:
                              maxVal = max(maxVal, dp[-k] + A[i])
              
                      dp.append(maxVal)
                      dp.pop(0)
                  return dp[-1]
              

              【讨论】:

              • 我认为这是最节省时间和空间的解决方案。干得好!
              【解决方案8】:

              100% c++ 解决方案( results)

              #include <climits>
              
              int solution(vector<int>& A) {
                  const int N = A.size();
                  if (N == 2)
                      return A[0] + A[1];
              
                  vector<int> MaxSum(N, INT_MIN);
                  MaxSum[0] = A[0];
                  for (int i = 1; i < N; i++) {
                      for (int dice = 1; dice <= 6; dice++) {
                          if (dice > i)
                              break;
                          MaxSum[i] = max(MaxSum[i], A[i] + MaxSum[i - dice]);
                      }
                  }
                  return MaxSum[N-1];
              }
              

              【讨论】:

              • 非常干净,我会通过只保留最后 6 个后向值来优化内存!
              【解决方案9】:

              100% 蟒蛇解决方案 借助上述答案和https://sapy.medium.com/cracking-the-coding-interview-30eb419c4c57

              def solution(A):
                  # write your code in Python 3.6
                  # initialize maxUntil [0]*n
                  n = len(A)
                  maxUntil = [0 for i in range(n)]
                  maxUntil[0]=A[0]
                  # fill in maxUntil, remember to chack limits
                  for i in range(1, n): # for each 
                      maxUntil[i] = maxUntil [i-1]
                      # check the max 6 to the left:
                      # for 1,..,6:
                      for dice in range(1,7):
                          if dice > i: # if dice bigger than loc - we are out of range
                              break
                          #else: check if bigger than cur elem, if so - update elem
                          maxUntil[i] = max(maxUntil[i],maxUntil[i-dice])
                      # add the current jump:
                      maxUntil[i] +=A[i]
                  # must reach the last sq:
                  return maxUntil[n-1]
                  
              

              【讨论】:

                【解决方案10】:

                我想解释一下我提出的算法,然后向你展示 C++ 中的实现。

                1. 为最大总和创建一个散列。我们只需要存储触手可及的元素,所以最后 6 个元素。这是因为骰子只能退回这么多。
                2. 为简单起见,使用数组中的第一个元素初始化哈希,因为此哈希中的第一个元素等于输入的第一个元素。
                3. 然后从第二个元素遍历输入元素。
                4. 对于每次迭代,从最后 6 个索引中找出最大值。将当前值添加到该值以获得当前最大总和。
                5. 当我们到达输入的末尾时,退出循环。
                6. 返回计算的最后一个元素的最大总和。为此,由于空间优化,我们需要使用模块进行裁剪

                此动态编程解决方案的运行时复杂度为O(N),因为我们遍历输入中的元素。如果我们考虑骰子范围 K,那么这将是 O(N * K)

                空间复杂度为O(1),因为我们有最后六个元素的哈希值。如果我们不考虑骰子面数不变,则为O(K),而是K。

                int solution(vector<int> &A)                                                    
                {                                                                               
                  vector<int> max_sums(6, A[0]);                                                
                  for (size_t i = 1; i < A.size(); ++i) max_sums[i % max_sums.size()] = *max_element(max_sums.cbegin(), max_sums.cend()) + A[i];
                  return max_sums[(A.size() - 1) % max_sums.size()];                            
                }
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2019-01-14
                  • 2012-10-29
                  • 2015-06-11
                  • 2012-03-26
                  相关资源
                  最近更新 更多