【问题标题】:Given an array of numbers. At each step we can pick a number like N in this array and sum N with another number that exist in this array给定一个数字数组。在每一步,我们都可以在这个数组中选择一个像 N 这样的数字,并将 N 与这个数组中存在的另一个数字相加
【发布时间】:2021-03-26 23:38:21
【问题描述】:

我被这个问题困住了。

给定一个数字数组。在每一步,我们都可以在这个数组中选择一个像 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();
}

这是我为这个问题编写的蛮力方法。有什么算法可以更快地解决这个问题吗?

【问题讨论】:

  • 问题的任何来源/参考?一个网址可能会有所帮助。
  • @DeepakTatyajiAhire 实际上,我提出了这个问题。我想找到解决这个问题的最佳算法。
  • @DeepakTatyajiAhire 我在谷歌上搜索过这个问题或任何类似的问题,但我没有找到任何对我有帮助的东西。
  • 这看起来像是一个 NP 完全问题。贪心算法不可能总是找到最佳解决方案。数组的最大大小是多少?
  • @Damien 实际上,这不是学校作业或竞赛问题。我正在寻找一种算法来尽快解决这个问题。

标签: algorithm math


【解决方案1】:

这有一种 np-complete 的感觉,但下面的搜索会在所有可能的归一化部分和中进行 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。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-20
    • 2022-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多