【问题标题】:How to solve this in an efficient way with optimum time complexity?如何以最佳时间复杂度有效地解决这个问题?
【发布时间】:2018-12-20 20:13:17
【问题描述】:

给定数组中的一组 N 个数字。给定 Q 查询。每个查询包含 1 个数字 x。

对于每个查询,你需要将x添加到数组的每个元素中,然后报告数组中的绝对值之和。

注意:对数组的更改是永久性的。有关更多说明,请参阅示例。

输入格式

第一行包含 N ,数组中的元素数。 下一行包含数组的 N 个空格分隔的整数。 下一行包含 Q(查询数)。 下一行包含 Q 个空格分隔的整数(数字 x)。

输出格式

对于每个查询,在换行符中输出总和。

约束
1≤N≤500000
1≤Q≤500000
-2000 ≤ 每个查询中的数量 ≤ 2000
-2000 ≤ 数组元素的值 ≤ 2000

示例输入

3
-1 2 -3
3
1 -2 3

样本输出

5
7
6

解释

在查询 1 之后:[ 0 , 3 , -2 ] => sum = 0 + 3 + 2 = 5

在查询 2 之后:[ -2 , 1 , -4 ] => sum = 2 + 1 + 4 = 7

在查询 3 之后:[ 1 , 4 , -1 ] => sum = 1 + 4 + 1 = 6

#include<stdio.h>
#include<stdlib.h>

int main()
{
  int n,*a,q,*aq;
  long int sum=0;
  scanf("%d",&n);
  a=(int*)malloc(sizeof(int)*n);
  for(int i=0;i<n;i++)
    scanf("%d",&a[i]);

  scanf("%d",&q);
  aq=(int*)malloc(sizeof(int)*q);
  for(int i=0;i<n;i++)
    scanf("%d",&aq[i]);

  for(int i=0;i<q;i++)
  {
    for(int j=0;j<n;j++)
    {
        sum+=abs(aq[i]+a[j]);
        a[j]=aq[i]+a[j];
    }

    printf("%ld\n",sum);
    sum=0;

  } 

}  

一些测试用例正在超时。

【问题讨论】:

  • 对于每个查询,数组中可能有分别保持负数、改变符号和保持正数的数字。如果您不遵循 Yves Daoust 的(狡猾的)建议对输入值进行分类(排序,在该词的一种解释中),请考虑对它们进行排序(sort 的另一种含义)。保留一个 value offset 以被每个查询修改(不要更新数组条目)和第一个非负条目的索引。对于每个查询,后者将是“有趣条目”范围的一端 - 在另一端搜索 -x(由偏移量修改)。

标签: algorithm performance logic


【解决方案1】:

您的解决方案正在执行 N.Q 操作,这是巨大的。

首先注意数据的范围是适中的,这样你就可以用4001个条目的直方图来表示N个数字。这个直方图是在 N 次操作中计算出来的(加上初始化 bin)。

然后,请求的总和作为每个 bin 的绝对差之和,由 bin 值加权。这将工作量从 N.Q 降低到 B.Q(B 是 bin 的数量)。


如果我是对的,我们可以通过将总和分解为负值的子和和正值的子和来做得更好。这些和是通过计算前缀和获得的。在 B 操作中预处理直方图之后,这应该会导致 Q 操作中的解决方案。

【讨论】:

    【解决方案2】:

    这是一个算法的概要:

    示例输入

    3
    -1 2 -3
    

    对数据进行排序并计算前缀和:

    -3, -1, 2
    -3, -4, -2 (prefix sums)
    

    (使用直方图作为 Yves Daoust suggested 将消除初始排序和任何二进制搜索以找到以下三个部分,这将显着优化复杂性。)

    维护一个正在运行的增量:

    delta = 0
    

    对于每个查询

    1 -2 3
    

    查询 1:

    * update delta:
      delta = 0 + 1 = 1
    
    * identify three sections:
      [negative unaffected] [switches sign] [positive unaffected]
           -3,   -1,                                 2
    
    * Add for each section abs(num_elements * delta + prefix_sum):
       abs(2 * 1 + (-4 - 0))        +        abs(1 * 1 + (-2 -(-4)))
       = abs(2 - 4) + abs(1 + 2)
       = 5
    

    查询-2:

    * update delta:
      delta = 1 - 2 = -1
    
    * identify three sections:
      [negative unaffected] [switches sign] [positive unaffected]
           -3,   -1,                                 2
    
    * Add for each section abs(num_elements * delta + prefix_sum):
       abs(2 * (-1) + (-4 - 0))     +     abs(1 * (-1) + (-2 -(-4)))
       = abs(-2 - 4) + abs(-1 + 2)
       = 7
    

    查询 3:

    * update delta:
      delta = -1 + 3 = 2
    
    * identify three sections:
      [negative unaffected] [switches sign] [positive unaffected]
           -3,                   -1,                   2
    
    * Add for each section abs(num_elements * delta + prefix_sum):
     abs(1 * 2 + (-3 - 0)) + abs(1 * 2 + (-4 - (-3))) + abs(1 * 2 + (-2 -(-4)))
       = abs(2 - 3) + abs(2 - 1) + abs(2 + 2)
       = 6
    

    样本输出

    5
    7
    6
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-02-21
      相关资源
      最近更新 更多