我有一些像
这样的元素的数组 5, 3, 7
和一个初始的int A
(例如在第一种情况下为5)。我必须对数组元素中的这个数字进行加法和减法,以得到小于或等于M
的最大数(在这种情况下为10)。中间操作的结果不能超过M
。我只能使用一次数组元素。
例如,这种情况的解决方案是
A = 5
A - 5 = 0
0 + 3 = 3
3 + 7 = 10
10等于M
,即最终结果。
可以用什么算法来解决这个问题?我正在用C ++制作这个程序。
答案 0 :(得分:1)
这是一种可能的算法:
A = 5
M = 10
highest number = 0
array of numbers = {5, 3, 7}
function()
call recursive function(empty string, index 0, A)
print highest number
recursive function(string of operations used so far, int current element index, int sum so far)
if the length of the string of operations == number of ints in array
if sum so far > highest number
highest number = sum so far
return
B = sum so far + current element
C = sum so far - current element
if B <= M
recursive function(string of operations plus appended '+', int current element index + 1, B)
if C <= M
recursive function(string of operations plus appended '-', int current element index + 1, C)
基本思想:使用递归来跟踪加法和减法运算。在这里,我只是将+
和-
附加到字符串中,当字符串达到数组的大小时,我检查了总和是否高于当前最高值。您可以在Java here中看到演示。您可以随意使用演示中的数字来了解它的工作原理。
答案 1 :(得分:1)
我不认为有一种“智能”算法可以在线性甚至多项式时间内解决这个问题。我担心唯一可行的方法是尝试所有可能的组合,并选择最好的组合。但是,您可以使用一些概念上类似branch and bound的技术来更快地获得结果。
我会这样做。
首先,我将生成所有可能的组合。对于您的每个N号码,您可以选择将其相加或减去它,因此您有2个选择。这意味着总体而言,您总共有2 ^ N个可能的选择。在您的示例中,当N = 3时,这些将是:---
,--+
,-+-
,-++
,+--
,+-+
,{ {1}},++-
。 (这就像二进制计数,从0 = 000到7 = 111)。
此时你必须“执行”每个序列。使用这些数字(+++
),第一个序列表示-5 -3 -7。您必须一次一步地运行它,并且每次检查您是否未超过目标M,因为它是必需的。由于您的初始值A为5,这将导致:5-5 = 0,并且因为0 <10,您可以继续。然后,0-3 = -3,小于10,所以没关系。然后,-3-7 = -10,因为这是最后一个操作,我们不必检查它是否小于10.所以序列是有效的,我们有5, 3, 7
的结果是 - 10。这是迄今为止最好的结果(因为它是唯一的结果),所以让我们将序列保存为最佳序列和最终结果。
然后,让我们转到下一个序列---
。再次它是有效的,最终结果是4.这是迄今为止最好的结果,所以我们可以保存这个序列 - +,覆盖以前的最佳结果。
继续,在--+
,我们会找到最好的结果,10,这是无法改进的,因此我们可以在那里停下来。如果我们没有达到M,我们必须评估所有序列,最后我们将得到最好的序列。可能有多个导致最佳结果,例如,如果您有重复的数字(-++
可以是+3 -3或-3 + 3,序列不同但结果显然是相同的),因此您可能希望将最佳解决方案存储在矢量中,而不是只保留一个。
这将是基本算法。现在,让我们尝试优化它。
到目前为止,我们已经生成了所有可能的序列(从3 3
到---
)并一次评估所有序列。但在某些情况下,评估它们都是浪费时间。例如,假设初始数字再次为A = 5,目标为M = 10。如果序列类似于+++
,那么我们会在某个时刻尝试评估6 3 4
,但这会立即失败,因为第一个中间结果将超过最大值(5 + 6 = 11)。按照基本算法,我们将继续评估+--
,但它没有意义:出于同样的原因,它显然会在同一步骤(第一个数字,即5 + 6)失败。无效序列以+-+
开头,并且所有连续的符号选择无关紧要: all 以+
开头的序列必然无效且可以被丢弃。所以这意味着如果我们找到一个无效的序列,我们就可以丢弃所有其他类似的序列。
为此,我们可以将所有序列保存在树中,这将每个符号选择与前一个符号相关联。然后,一旦我们找到无效的解决方案,我们就可以丢弃所有其他类似的解决方案。例如,树可以保存为:
+
并且每个序列对应于必须探索以到达其中一个叶子的一系列节点。当您访问树时,对于每个节点,您可以计算并存储到目前为止的序列值。因此,当您在第一级达到+时,您会看到5 + 6 = 11,这太多了,并且您将整个节点标记为无效,从而避免探索所有可能的变体。当然,对于N = 3个数字,它将没有太大的区别,因为总共只有2 ^ 3 = 8个序列。但是对于N = 20,他们将是一百万,节省的时间可能很大。