Задан массив чисел. На каждом шаге мы можем выбрать число, подобное N, в этом массиве и суммировать N с другим числом, существующим в этом массиве

#algorithm #math

#алгоритм #математика

Вопрос:

Я застрял на этой проблеме.

Задан массив чисел. На каждом шаге мы можем выбрать число, подобное N, в этом массиве и суммировать N с другим числом, существующим в этом массиве. Мы продолжаем этот процесс, пока все числа в этом массиве не будут равны нулю. Какое минимальное количество шагов требуется? (Мы можем гарантировать, что изначально сумма чисел в этом массиве равна нулю).

Пример: -20, -15,1,3,7,9,15

  • Шаг 1: выберите -15 и суммируйте с 15 -> -20,0,1,3,7,9,0
  • Шаг 2: выберите 9 и суммируйте с -20 -> -11,0,1,3,7,0,0
  • Шаг 3: выберите 7 и суммируйте с -11 -> -4,0,1,3,0,0,0
  • Шаг 4: выберите 3 и суммируйте с -4 -> -1,0,1,0,0,0,0
  • Шаг 5: выберите 1 и суммируйте с -1 -> 0,0,0,0,0,0,0

Итак, ответ в этом примере равен 5.

Я пробовал использовать жадный алгоритм. Это работает следующим образом:

На каждом шаге мы выбираем максимальное и минимальное число, которое уже доступно в этом массиве, и суммируем эти два числа, пока все числа в этом массиве не будут равны нулю.

но это не работает и дает мне неправильный ответ. Кто-нибудь может помочь мне решить эту проблему?

 #include <bits/stdc  .h>

using namespace std;

int a[] = {-20,-15,1,3,7,9,15};

int bruteforce(){
    
    bool isEqualToZero = 1;
    for (int i=0;i<(sizeof(a)/sizeof(int));i  )
        if (a[i] != 0){
            isEqualToZero = 0;
            break;
        }
        
    if (isEqualToZero)
        return 0;
    int tmp=0,m=1e9;
    
    for (int i=0;i<(sizeof(a)/sizeof(int));i  ){
        for (int j=i 1;j<(sizeof(a)/sizeof(int));j  ){
            if (a[i]*a[j] >= 0) continue;
            tmp = a[j];
            a[i]  = a[j];
            a[j] = 0;
            
            m = min(m,bruteforce());
            
            a[j] = tmp;
            a[i] -= tmp;
        }
    }
    
    return m 1;
}

int main()
{
    cout << bruteforce();
}
 

Это подход грубой силы, который я написал для этой проблемы. Есть ли какой-либо алгоритм для более быстрого решения этой проблемы?

Комментарии:

1. Любой источник / ссылка на проблему? Может помочь URL.

2. @DeepakTatyajiAhire На самом деле, я создал эту проблему. Я хочу найти наилучший алгоритм для решения этой проблемы.

3. @DeepakTatyajiAhire Я искал в Google об этой проблеме или любой подобной проблеме, но я не нашел ничего, что помогло бы мне.

4. Это похоже на NP-полную задачу. Маловероятно, что жадный алгоритм всегда может найти наилучшее решение. Каков максимальный размер массива?

5. @Damien На самом деле, это не школьное домашнее задание или задача для конкурса. Я ищу алгоритм для решения этой проблемы как можно быстрее.

Ответ №1:

Это имеет np-полное ощущение, но следующий поиск выполняет поиск A * по всем возможным нормализованным частичным суммам на пути к одному ненулевому члену. Это решает вашу проблему и означает, что вы не попадаете в бесконечный цикл, если сумма не равна нулю.

Если жадность работает, сначала будет изучен жадный путь, убедитесь, что вы не можете сделать лучше, и вернитесь довольно быстро. Если жадность не работает, это может…займет намного больше времени.

Реализация на Python, потому что это легко для меня. Перевод на другой язык — это упражнение для читателя.

 import heapq

def find_minimal_steps (numbers):
    normalized = tuple(sorted(numbers))
    seen = set([normalized])
    todo = [(min_steps_remaining(normalized), 0, normalized, None)]

    while todo[0][0] < 7:
        step_limit, steps_taken, prev, path = heapq.heappop(todo)
        steps_taken = -1 * steps_taken # We store negative for sort order
        if min_steps_remaining(prev) == 0:
            decoded_path = []
            while path is not None:
                decoded_path.append((path[0], path[1]))
                path = path[2]
            return steps_taken, list(reversed(decoded_path))
        prev_numbers = list(prev)
        for i in range(len(prev_numbers)):
            for j in range(len(prev_numbers)):
                if i != j:
                    # Track what they were
                    num_i = prev_numbers[i]
                    num_j = prev_numbers[j]

                    # Sum them
                    prev_numbers[i]  = num_j
                    prev_numbers[j] = 0

                    normalized = tuple(sorted(prev_numbers))
                    if (normalized not in seen):
                        seen.add(normalized)
                        heapq.heappush(todo, (
                            min_steps_remaining(normalized)   steps_taken   1,
                            -steps_taken - 1, # More steps is smaller is looked at first
                            normalized,
                            (num_i, num_j, path)))

                    # set them back.
                    prev_numbers[i] = num_i
                    prev_numbers[j] = num_j

print(find_minimal_steps([-20,-15,1,3,7,9,15]))
 

Для развлечения я также добавил реализацию связанного списка, которая не просто сообщает вам, сколько минимальных шагов, но и какие из них он нашел. В этом случае его шаги (-15, 15), (7, 9), (3, 16), (1, 19), (-20, 20) означали добавление 15 к -15, от 9 до 7, от 16 до 3, от 19 до 1 и от 20 до -20.