【问题标题】:Cheapest cost through an array通过阵列最便宜的成本
【发布时间】:2017-05-27 17:42:52
【问题描述】:

问题

给定一个长度为 [N] 的数组,您必须从数组 [0] 开始并遍历到末尾。您可以移动一个或两个位置,即 array[0] -> array[1] 或 array[0] -> array[2] 取决于哪个数字总和较低。这将一直重复到最后,并且必须包含 array[N]。

[1, 10, 3, 8, 4] 最便宜的导航方式是 = 8 via array[0] + array[2] + array[4]

我目前的解决方案:

int totalCost = 0
totalCost += array[0]
int i = 1;
while (i < array.length)
{
    if (i + 1 < array.length)
    {
        int sum1 = totalCost + array[i];
        int sum2 = totalCost + array[i + 1];
        if (sum1 < sum2)
        {
            totalCost += array[i];
            i++;
        }
        else
        {
            totalCost += array[i + 1];
            i += 2;
        }
    }
    else
    {
        totalCost += array[i];
        i++;
    }
}

这似乎适用于大多数数组...如果早期跳转会导致更大的数字,但允许更好地通过数组进一步跳转,最终会导致更低的数字,那么问题就来了。我不知道如何处理。

【问题讨论】:

  • 那么您如何确定在较早时间进行较长时间的跳跃会导致较低的总体成本?只有真正完成整个路线,对吧?
  • 数组中可以有负数吗?
  • @RomanPekar 我不这么认为,我唯一知道的是不允许我对数组进行排序。
  • @UnholySheep 我同意,我必须遍历整个数组才能查看所有可能的组合。我曾考虑过类似的问题,例如用阶乘来确定可能组合的总数,但这与此不完全相同。我也在考虑拥有一个包含所有可能总和的参考数组。仍然不确定我将如何考虑所有可能的选择。

标签: c# arrays algorithm recursion theory


【解决方案1】:

你的方法行不通,因为当你处于某个元素时,你试图决定下一步行动。关键是,在您知道数组末尾的第二个元素之前的所有元素值之前,您通常无法决定下一步行动。

不仅在计算机科学中,而且通常回顾过去和学习比展望未来和预测更容易。因此,只要可以从历史数据中解决问题或子问题,就不要试图以未来的期望来解决它。

现在,让我们看看如何将其应用于您的问题。

如果您在数组中的位置i,您所做的就是通过查看可能的后续步骤并决定其中一个来尝试预测正确的方式,这无法可靠地工作。因此,让我们假设您在位置i,并且您不想知道下一步要去哪里,而是问“到达当前位置的最佳(成本效益)方法是什么以及它有多少成本?”

对于第一个位置i=0,这是微不足道的。您通过启动算法到达此位置,成本等于a[0] 的值。

对于第二个位置i=1,也是微不足道的。您可以以 1(小步)或 2(大步)的步长移动,但在这种特定情况下,只有一小步是可能的,因此您从位置 i=0 到达 i=1,成本等于成本到达i=0 加上位置值i=1

对于所有后续位置i&gt;1,需要做出决定,是通过小步还是大步到达当前位置。如果小步到达当前位置,其成本计算为到达i-1 的成本加上位置i 的值。其他情况,如果当前位置大步到达,其成本计算为到达i-2的成本加上位置i的值。为了以最低的成本到达位置i,通过比较它们的相关成本并选择最小值来决定小步和大步。

当到达数组的末尾时,将计算到达每个位置的成本和步数,并返回到达最后一个位置的成本。

如果您只需要最小成本而不需要实际路径,那么以下应该可以工作(c 相关的伪代码):

costs[array.size] = { 0 };

for (i = 0; i < array.size; ++i)
{
    if (i > 1) costs[i] = array[i] + min(costs[i-1], costs[i-2]);
    else if (i > 0) costs[i] = array[i] + costs[i-1];
    else costs[i] = array[i];
}

result = costs[array.size - 1];

它基本上说:到达一个点i 可以通过从前一点或从前两点来完成。如果已经计算了前两个点的成本,则决策就像取前两个点成本中的最小值并添加当前点成本一样简单。

您甚至可以使用总共 3 个变量来代替包含所有子成本的数组(丢弃代表低于i-2 的索引的子成本),但是路径不能从子成本。

【讨论】:

  • 这似乎没有考虑到如果成本较低则允许移动到 array[i + 1] 的可能性。我是不是弄错了,读错了?
  • @JayGuyll 我认为你读错了。这种方法通常不会尝试预测任何内容(i + 1),而是通过访问先前的成本(i - 1i - 2)来计算当前点的成本。 i - 1 相当于你的 i + 1 一步一步。
【解决方案2】:
var c = new List<int>();
for (int i = 0; i < a.Length - 1;)
{
    c.Add(i);
    if (i < a.Length - 2 && a[i + 2] < a[i + 1])
        i += 2;
    else
        i += 1;
}
c.Add(a[a.Length - 1]);

【讨论】:

    【解决方案3】:

    我忽略了未来总和受到早期选择影响的可能性。我同意这是某种程度的人工智能。

    我修改为的解决方案。

    int total = 0;
    cost += array[0];
    
    int i = 0;
    while (i < array.Length - 1)
    {
        if ((array[i+1] + total) < array[i+2] + total) && (i != array.Length - 3))
        {
            total += array[i + 1];
            i++;
        }
        else
        {
            total += array[i + 2];
            i += 2;
        }
    }
    

    我必须添加倒数第二个元素检查,因为如果迭代器落在倒数第二个,没有理由检查哪个更低,因为无论如何我都必须选择最后一个。

    【讨论】:

    • 解决方案如何?这是超出范围的异常,因为您检查i &lt; array.Length,然后访问array[i + 1]array[i + 2]
    • 另外,检查序列2, 1, 4, 7, 6,我认为你的结果(如果不是以异常结尾)将是13,但正确的结果是12
    • @grek40 你是最正确的,在检查 i
    • 我不确定您是如何在上面的数组中获得 12 的。我也将其计算为 13。如果您必须默认包含 2 和 6,那么我将采用的路径是 2、1、4、6 = 13。
    • 好吧,也许我理解错了,但是为什么不去2, 4, 6这个选择有特殊的规则要求吗?
    猜你喜欢
    • 2021-11-26
    • 2023-04-11
    • 2019-07-28
    • 1970-01-01
    • 2017-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多