【问题标题】:Find numbers of subarray of an array whose sum is divided by given number [duplicate]查找总和除以给定数字的数组的子数组的数量[重复]
【发布时间】:2013-11-01 16:50:45
【问题描述】:

我陷入了一个算法问题。请为以下问题推荐一些有效的算法。

问题是

找出总和可被给定数整除的子数组数。

我的工作

我做了一个算法,复杂度是O(N^2),这里,N = 数组的大小。

我的代码

#include<stdio.h>

using namespace std;

 main() {
    int N;
    int P;
    int T;
    int val;
    long long int count = 0;
    long long int answer = 0;
    scanf("%d", &T);
    //T = 20;

    for(int k = 1; k <= T; k++) {
        scanf("%d", &N);
        scanf("%d", &P);
        count = 0;
        answer = 0;
        for(int i = 0; i < N; i++) {
            scanf("%d", &val);
            count += val;
            workingArray[i] = count;
        }

        for(int length = 1; length <= N; length++) {
            for(int start = 0; start <= (N-length); start++) {
                if( start == 0 ) {
                    if(workingArray[start+length-1]%P == 0) answer++;
                }
                else if( (workingArray[start+length-1] - workingArray[start-1])%P == 0) answer++;
            }
        }

        printf("Case #%d\n%lld\n", k, answer);
    }
    return 0;
 }

【问题讨论】:

  • 你写的代码到底有什么问题?
  • 我认为你的解决方案跳过了很多可能的组合......你只在这里检查相邻元素的总和(除非你的问题中定义了“子数组”)
  • @Ashalynd “子数组”通常指的是数组的连续部分(例如 maximum subarray problem)。对于不连续的部分,人们通常谈论一个“子集”(例如subset sum problem)。
  • @Ashalynd 我拿走了所有案例,没有遗漏任何案例。这里,子数组表示连续元素。
  • 我明白了,感谢您的澄清。

标签: c++ arrays algorithm math numerical-analysis


【解决方案1】:

对于给定的号码X...

基本思想:(带有非正式的正确性证明)

如果[a, b] 范围内的数字之和可以被X 整除,则:

(∑<sub>i=1 to a-1</sub>input[i]) % X = (∑<sub>i=1 to b</sub>input[i]) % X

用较少的数学术语:

the sum from the first element to b = the sum from the first element to a
                                    + the sum of the elements between the two

所以:

the sum of the elements between the two = the sum from the first element to b
                                        - the sum from the first element to a

然后,如果右边的这些和除以X 时具有相同的余数,则余数将抵消并且两者之间的元素之和将被X 整除。细说:

C = the sum of the elements between the two
B = the sum from the first element to b
A = the sum from the first element to a

现在我们可以将B 转换为PX + QARX + S 的形式,对于一些整数PQRS,与0 &lt;= Q, S &lt; X .这里,根据定义,QS 将分别是 BA 除以 X 的余数。

那么我们有:

C = (PX + Q) - (RX + S)
C = PX + Q - RX - S
C = PX - RX + Q - S
C = (P-R)X + Q - S

显然(P-R)X 可以被X 整除(结果就是(P-R))。现在我们只需要Q - S 可以被X 整除,但是由于0 &lt;= Q, S &lt; X,它们需要相等。

例子:

B = 13A = 7X = 3

这里是B % X = 1A % X = 1

我们可以将B 重写为4*3 + 1,将A 重写为2*3 + 1

然后是C = 4*3 + 1 - 2*3 - 1 = 2*3,它可以被3整除。

高级方法:

构造一个key -&gt; value 的哈希映射,其中每个值表示您可以从数组的开头开始并在某个给定位置结束的方式,加起来为sum mod X = key(参见“Mod 3”下例中的线和地图值)。

现在,根据上面的逻辑,我们知道如果两个子数组分别从ab的位置开始和结束,都具有相同的sum mod X,则子数组[a, b]将被整除X.

因此,哈希图中的每个值都表示一组可能的起点和终点的大小,这将为我们提供一个可被X 整除的子数组(任何点都可以是起点或终点)。

选择这些起点和终点的可能方法的数量很简单
value choose 2 = value!/(2*(value-2)!)(如果值为 1,则为 0)。

因此,我们计算哈希图中的每个值并将它们全部相加,以获得可被X 整除的子数组的数量。

算法:

构造一个哈希映射,它将存储迄今为止所有数字的累积和 mod X 映射到该余数出现频率的计数(在预期的 O(n) 中构建)。

0 的值加一 - 这对应于数组的开头。

将计数初始化为 0。

对于哈希图中的每个值,将value!/(2*(value-2)!) 添加到计数中。

计数是所需的值。

运行时间:

预期为O(n)

示例:

Input:    0  5  3  8  2  1
X = 3

Sum:   0  0  5  8 16 18 19
Mod 3: 0  0  2  2  1  0  1

Map:
  0 -> 3
  1 -> 2
  2 -> 2

Count = 3! / 2*(3-2)! = 3  +
        2! / 2*(2-2)! = 1  +
        2! / 2*(2-2)! = 1
      = 5

子数组将是:

0  5  3  8  2  1
-                     0                 =  0 % 3 = 0
-------------         0 + 5 + 3 + 8 + 2 = 18 % 3 = 0
   ----------         5 + 3 + 8 + 2     = 18 % 3 = 0
      -               3                 =  3 % 3 = 0
            ----      2 + 1             =  3 % 3 = 0

【讨论】:

  • 您只是考虑了连续元素,而问题只是询问了一些子数组,我将其视为子序列样式而不是子字符串格式,例如您没有采用 3+2+1 或 0 +3+2+1,顺便说一下,我不知道保留订单是否重要,例如我不知道我们应该数3+2+1,6次还是一次。 (我想后一种情况是可以接受的)。
  • @SaeedAmiri "subarray" 通常指的是数组的连续部分,例如maximum subarray problem.
  • No 不是这样的,例如在您的问题 statement 中非常清楚地指出 "contiguous subarray",而问题名称是通用的(其中应该很短,所以没有必要以问题的名义写下所有细节)。在问题定义中,没有关于连续性的争论。所以我们应该假设这不会继续。
  • @SaeedAmiri 好吧,不管怎样,OP just clarified 它是连续元素。
  • @Dukeling 我理解你解决这个问题的方式。但是我没感觉到。您能否解释一下,您如何从非常基本的步骤构建此逻辑。提前致谢。
【解决方案2】:

我可能有一个更简单的解决方案。在 O(n) 时间和 O(n+k) 空间中。其中 n 是数组的大小,k 是我们检查可分性的数字。

认为数组为A[n],个数为K

  1. 创建另一个数组 SUM_TILL_NOW[n]。
  2. 对于每个 A[i] 填充 SUM_TILL_NOW [i]= SUM_TILL_NOW[i-1]+A[i] %K; (SUM_TILL_NOW[0]= A[0])
  3. 在这个新数组中找到两个相等的数字。

为此,创建一个大小为 K 的新数组 CHECK[]。

遍历 SUM_TILL_NOW 数组并检查是否设置了 CHECK[SUM_TILL_NOW[i]]。

如果不设置为 i。

else CHECK[SUM_TILL_NOW[i]],i 是和可被 K 整除的子集。

下面是一个相同的c++函数。

#include <iostream>
#include <string.h>

using namespace std;

void printrange(int* A, int N, int K)
{
    int STN[N], C[K];
    memset(C, -1, K*sizeof(int));
    int i;
    int sum=A[0];
    STN[0]= (A[0]%K);
    for (i= 1; i< N; i++)
    {
        sum+= A[i];
        STN[i]= sum%K;
    }
    for(i=0; i< N; i++)
    {
        if(C[STN[i]] == -1)
            C[STN[i]] =i;
        else
        {
            cout<< C[STN[i]]+1 <<" "<< i;
            break;
        }
    }
}

int main()
{
    int A[]= {6, 9, 2, 1, 8, 6, 2, 5};
    printrange(A, sizeof(A)/sizeof(A[0]), 7);
    return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-01-12
    • 1970-01-01
    • 2023-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-11
    相关资源
    最近更新 更多