通过删除数字计算最大分数的算法

时间:2018-08-24 17:36:42

标签: java algorithm

我正在阅读此leetcode algorithm,自2天以来我已经多次阅读说明,试图理解它,但是我无法理解如何解决程序的概念。

这是问题陈述:

  

给定一个由整数组成的数组,您可以对   数组。

     

在每个操作中,您选择任意数字[i]并将其删除以赚取数字[i]   点。之后,您必须删除等于nums [i]-1或   nums [i] + 1。

     

您从0分开始。返回您可以获得的最大积分   通过应用此类操作来赚钱。

     

示例1:输入:nums = [3,4,2]输出:6说明:删除4至   赚4点,因此3也被删除了。然后,删除2赚取   2分。总共获得6分。

以下是解决方法的说明:

算法

  

对于数字的唯一值k(以递增顺序),让我们保持   避免和使用的正确值,代表正确的答案   我们不分别取k。

     

如果新值k与先前的最大值prev相邻,   那么我们必须取k的答案是(k的点值)+避免,   而如果不取k的答案是max(避免使用)。   同样,如果k不与prev相邻,则答案为必须取k   是(k的点值)+ max(避免使用),如果   不能取k为max(避免使用)。

     

最后,最佳答案可能会或可能不会使用的最大值   nums,所以我们返回max(avoid,using)。

和相应的Java程序:

public int deleteAndEarn(int[] nums) {
        int[] count = new int[10001];
        for (int x: nums) count[x]++;
        int avoid = 0, using = 0, prev = -1;

        for (int k = 0; k <= 10000; ++k) if (count[k] > 0) {
            int m = Math.max(avoid, using);
            if (k - 1 != prev) {
                using = k * count[k] + m;
                avoid = m;
            } else {
                using = k * count[k] + avoid;
                avoid = m;
            }
            prev = k;
        }
        return Math.max(avoid, using);
    }

我无法理解这里如何使用avoidusing变量以及它如何解决问题陈述。

能否请您帮助我理解这一点。

1 个答案:

答案 0 :(得分:0)

我在该页面上解决了这个问题。 该算法基于一个很好的技巧。在应用算法之前先处理输入时,很容易解决此问题。 输入被重新组织成存储桶。

假设您输入了1 1 2 3 3 2 2 4 4 4

您可以将其重组为

(Two 1s), (Three 2s), ( Two 3s), (Three 4s)

现在根据问题,如果您选择任何存储桶,则应放弃包含(bucket-element-value-1)和(bucket-element-value + 1)的存储桶,它们实际上是与此存储桶相邻的存储桶。

现在问题归结为您如何在无法获得相邻存储桶值的约束条件下如何选择存储桶以获取最大总和?

这很简单-遍历存储桶时,您可以使用存储桶执行两项操作。避免使用它。

如果避免这种情况,您可以达到的最大金额是多少-这取决于您对上一个所做的事情。

amountWhenAvoided[i] = Math.max( amountWhenAvoided[i-1], amountWhenTaken[i-1] );

如果使用它,可以达到的最大金额是多少?您将获得该存储分区的值+离开/避免上一个存储分区时所获得的任何值。

amountWhenTaken[i] = amountWhenAvoided[i-1] + valueOfBucket[i]

到达水桶尽头的答案将是:

Math.max( amountWhenTaken[n], amountWhenAvoided[n] )

您可以这样编码:

public int deleteAndEarn( int[] nums ) {
  //reorganize.
  int[] valueOfBucket= new int[10001] //This is the maximum size of the buckets.

  for ( int num : nums ) {
     valueOfBucket[num] += num; //Populate each bucket.
  }

  //Now go through each bucket - remember - a bucket could be empty that's fine.
  int[] amountWhenTaken = new int[n];
  int[] amountWhenAvoided = new int[n];
  amountWhenTaken[0] = 0; //Because there are no buckets to start with - buckets start from 1
  amountWhenAvoided[0] = 0;
  for ( int i = 1; i <= n; i++ ) {
     amountWhenAvoided[i] = Math.max( amountWhenAvoided[i-1], amountWhenTaken[i-1] );
     amountWhenTaken[i] = amountWhenAvoided[i-1] + valueOfBucket[i];
  }
  return Math.max( amountWhenTaken[n], amountWhenAvoided[n] );
}

如果您观察上面的代码,则实际上不需要对数组进行操作。由于数组中的当前元素取决于其先前的元素,因此我们只需两个变量就可以做到这一点,将其称为avoiding, using 然后第二个for循环可以写成:

public int deleteAndEarn( int[] nums ) {
      //reorganize.
      int[] valueOfBucket= new int[10001] //This is the maximum size of the buckets.

      for ( int num : nums ) {
         valueOfBucket[num] += num; //Populate each bucket.
      }

      //Now go through each bucket - remember - a bucket could be empty that's fine.
      int using_prev = 0;
      int avoiding_prev = 0;
      int avoiding_curr = 0;
      int using_curr = 0;
      for ( int i = 1; i <= n; i++ ) {
         avoiding_curr = Math.max( avoiding_prev, using_prev);
         using_curr = avoiding_prev + valueOfBucket[i];
         avoiding_prev = avoiding_curr;
         using_prev = using_curr;
      }
      return Math.max( avoiding_curr, using_curr );
 }