Алгоритм вычисления максимума баллов путем удаления числа

Я просматриваю этот алгоритм leetcode, я пытаюсь понять его, читая объяснение много раз за 2 дня, но я не могу понять, как решается программа.

Это постановка проблемы:

Given an array nums of integers, you can perform operations on the array.

In each operation, you pick any nums[i] and delete it to earn nums[i] points. After, you must delete every element equal to nums[i] - 1 or nums[i] + 1.

You start with 0 points. Return the maximum number of points you can earn by applying such operations.

Example 1: Input: nums = [3, 4, 2] Output: 6 Explanation: Delete 4 to earn 4 points, consequently 3 is also deleted. Then, delete 2 to earn 2 points. 6 total points are earned.

Вот объяснение того, как это решается:

Алгоритм

For each unique value k of nums in increasing order, let's maintain the correct values of avoid and using, which represent the answer if we don't take or take k respectively.

If the new value k is adjacent to the previously largest value prev, then the answer if we must take k is (the point value of k) + avoid, while the answer if we must not take k is max(avoid, using). Similarly, if k is not adjacent to prev, the answer if we must take k is (the point value of k) + max(avoid, using), and the answer if we must not take k is max(avoid, using).

At the end, the best answer may or may not use the largest value in nums, so we return 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);
    }

Я не могу понять, как здесь используются переменные avoid и using и как они решают постановку проблемы.

Не могли бы вы помочь мне понять это.

Вы прошли код с помощью отладчика, чтобы посмотреть, как работает «целевая функция»?

Micromuncher 24.08.2018 20:39

Вы не понимаете объяснения того, как она решается или как код ей соответствует?

juvian 24.08.2018 20:46

@Micromuncher, да, пробовал отладку с использованием eclipse IDE

learner 24.08.2018 21:38

@juvian, я понял код, основываясь на объяснении. Но я не могу понять объяснение.

learner 24.08.2018 21:38
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
4
590
1

Ответы 1

Я рассмотрел проблему на этой странице. В основе алгоритма лежит очень красивый трюк. Эту проблему очень легко решить, если вы сначала обрабатываете ввод перед применением алгоритма. Вход реорганизован в сегменты.

Допустим, у вас есть ввод: 1 1 2 3 3 2 2 4 4 4

Вы можете реорганизовать его в

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

Теперь, согласно проблеме, если вы выберете любое ведро, вы должны отказаться от ведра, содержащего (значение-элемента-ведра - 1) и (значение-элемента-ведра + 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 );
 }

Другие вопросы по теме