【问题标题】:Create least cost array创建成本最低的数组
【发布时间】:2015-07-13 04:02:55
【问题描述】:

我有一个大小为 n+1 的数组,我希望在其中存储购买 n 项目的最低成本(i^th 索引存储 i^th 项目的成本)。

m不同的卖家:每个卖家提供物品L到物品R(其中L,R>=1 和L,RC 每个。

为了创建成本最低的数组,我做了以下操作:

for (int i=1; i<=m; i++) {
    int L = L of i^th seller
    int R = R of i^th seller
    int C = selling price of i^th seller
    for (int j=L; j<=R; j++) {
        if (leastCost[j]==0 || leastCost[j]>C) {
            leastCost[j]=C
    }
}

构造这个数组是O(n*m),访问这个数组是O(1)

对于非常大的nm 值,是否有更好的方法来构造成本最低的数组?

也许是一种不同的方法,不将其存储在数组和其他东西中,从而降低整体时间复杂度?

【问题讨论】:

  • 我不知道你从哪里得到O(n^2)。我得到 O(n * m)。这是完全不同的。当您考虑实际需要访问(读取)多少元素时,这应该是最佳的。除非有任何额外的信息你必须开始......
  • 我更正了。这真的是最优的吗?即使对于非常大的 n 和 m?没有什么可以补充的了。
  • @Aron 太高了?是的,Aron 是对的。您的卖家表有m 卖家和n 价格(平均)。所以这是n*m。您需要至少遍历每个元素一次,因此(n*m) 是最佳选择(除非您有量子计算机)。
  • 可以使用Segment tree,所以创建树的时间复杂度是O(m log n),每次查询都是O(log n)
  • @Gsquare 最好的优化是使用我在我的解决方案中展示的贪婪方法。

标签: arrays algorithm data-structures


【解决方案1】:

当然,我们可以做得比 O(n*m) 更好,而且解决方案也很简单。

以下是解决这个问题的伪代码:

Construct a MIN-HEAP of the sellers based on their costs c.

Construct another array x[1...n] with its each element set to 0 initially.

Do the following initializations:
 count=0

while(count < n)
{
 S = EXTRACT_MIN(Heap)
 if(count==0)
 {            
  for(j=S.L to S.R)
  {
   leastCost[j]=S.c
   ++count
   x[j]=S.R
  } 
 }
 else
 {
  for(j=S.L;j<=S.R;++j)
  {
   if(x[j]!=0)
   {
    i=x[j] and continue;
   }
   else 
   {
    leastCost[j]=S.c
    ++count
    x[j]=S.R
   }
  }
 }
}

说明

我们可以在这个问题中实现的最佳优化是我们跳过所有已经填充的数组索引,因为它们已经具有最低成本。

x 辅助数组:数组 x 帮助我们跳过所有已经填充的数组索引,因为:

x[i] 存储索引 j 使得 i 到 j 已经在数组中填充了最少的成本,因此这就是使用条件语句的原因:

if(x[i]!=0)
{
 i=x[i] and continue;
}

所以基本上它帮助我们直接跳转到未填充的索引。

MIN-HEAP:它允许我们在 O(logm) 时间内找到所有成本中的最小成本 c。

时间复杂度

由于我们访问数组 minimumCost 最多 n 次因此访问数组:O(n)

构建堆需要 O(m)

在最坏的情况下,所有卖家都会为一些索引做出贡献,并且会有精确的 m 个 EXTRACT_MIN(Heap) 操作,每个操作都需要 O(logm) 时间,所以时间是:O (m*logm).

因此总时间复杂度=O(n + mlogm + m) = O(n + mlogm)

顺便说一句,如果我使用 C 语言,我会为卖方使用结构,如果是 Java,则为卖方使用一个类。如有任何疑问,请随意。

【讨论】:

  • @PhamTrung 请允许我清除您的疑问。每次执行内部循环时,它都会增加控制变量计数的外部循环的变量。这就是我的时间复杂度的原因。
  • @PhamTrung 嵌套循环并不总是意味着二次时间。只有当控制内循环和外循环的变量相互独立时,它才会是二次的。
  • 我明白了,但是您如何处理这种情况? (5 , 5 , 1) , (4, 5, 2) , (3, 5 , 3) .... (1, 5 , 5), (5, 6, 7) .... (5,10 , 10) . n = 10,每个卖家表示为(L,R,C)。显然,总时间复杂度将是 2*(1 + 2 + ... + n/2) = O(n^2) 在迭代时,您只能更新一项,您仍然需要遍历所有范围.
  • 时间复杂度是 2*(1 + 2 + ... + n/2) * log m = O(n ^2 log m) 对于上述情况。
  • @PhamTrung 请修改你的基础知识。大 O 是用于渐近分析的符号,而不是您在表达式中计算的符号。
【解决方案2】:

你可以在这里使用某种形式的堆栈

第一次按 L 对所有卖家进行排序,如果 L 相等则按 C desc 排序。

然后从第一个卖家(和李敏)开始

如果堆栈为空,则将他放入堆栈。如果没有更多卖家有

(Lj == Li)

然后cost[Li] = C[i]Li++(在堆栈中)

如果有Lj == Li 的条目,那么

  • 如果Rj &lt; RiCj &gt; Ci跳过

  • 如果 Rj &lt; RiCj &lt; Ci 将此卖家添加到堆栈

  • 如果Rj &gt; RiCj &gt; Ci 则弹出当前卖家(i),添加此卖家(j)并添加卖家(i)

【讨论】:

  • 我不确定这是如何工作的。你能给出一些工作代码或更好的伪代码吗?
【解决方案3】:

这个问题是一个经典的问题Range minimum query,你可以使用Segment tree得到一个时间复杂度为O(m log n)来创建树和O(log n)的每个查询的解决方案。

更多细节:

我们用一棵树来表示每件商品从 1 到 n 的最低价格。

对于每个卖家,我们更新树:

tree.update(L, R, C);

最后,为了获得项目i 的最小值,我们查询树:

tree.getMin(i, i);

由于updategetMin的时间复杂度都是O(log n),所以整个程序的时间复杂度是O(max(m,n) log n)。您无需修改​​原始分段树实现中的任何内容。

【讨论】:

  • 我没有范围查询。如何为这种特殊情况构建段树?
  • @Gsquare : each seller provides item L to item R : 从 L 到 R,更新值 C(如果可能),这些是范围查询。而对于每一项i,查询是取范围(i, i)的最小值,其时间复杂度为O(log n)。
猜你喜欢
  • 1970-01-01
  • 2014-02-21
  • 2023-03-20
  • 1970-01-01
  • 2021-09-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多