我遇到了一个算法问题。请为我提出一些针对以下问题的有效算法。
问题
查找其数量可以被给定数字整除的子数组。
我的工作
我做了一个算法,其复杂度为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;
}
答案 0 :(得分:23)
对于给定的数字X
...
基本理念:(非正式的证明)
如果[a, b]
范围内的数字总和可以被X
整除,那么:
(∑i=1 to a-1input[i]) % X = (∑i=1 to binput[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 + Q
,我们可以将A
转换为RX + S
和P
形式转换为Q
形式, R
和S
,0 <= Q, S < X
。在这里,根据定义,Q
和S
将是B
和A
除以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 <= Q, S < X
起,它们就必须相等。
示例:
允许B = 13
,A = 7
,X = 3
。
此处B % X = 1
和A % X = 1
。
我们可以将B
重写为4*3 + 1
,将A
重写为2*3 + 1
。
然后C = 4*3 + 1 - 2*3 - 1 = 2*3
,可以被3
整除。
高级方法:
构造一个key -> value
的哈希映射,其中每个值表示从数组的开头开始的多少种方式,最终在某个给定的位置加起来sum mod X = key
(参见“Mod 3”行和下面示例中的地图值。)
现在,基于上面的逻辑,我们知道如果两个子阵列从头开始并分别在位置a
和b
结束,两者都具有相同的sum mod X
,则子阵列{ {1}}将被[a, b]
整除。
因此,散列图中的每个值都代表一组可能的起点和终点的大小,它们将为我们提供一个可被X
整除的子阵列(任何点都可以是起点或终点)。 / p>
选择这些起点和终点的可能方式的数量简单为
value choose 2 = value!/(2*(value-2)!)
(如果值为1则为0)。
因此,我们计算哈希映射中的每个值,并将它们全部添加,以获得可被X
整除的子数组的数量。
<强>算法:强>
构造一个哈希映射,它将存储迄今为止所有数字的累积和X
映射到剩余值出现的频率计数(在预期的mod X
中构建)。
将O(n)
的值增加1 - 这对应于数组的开头。
将计数初始化为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
答案 1 :(得分:2)
我可能有一个更简单的解决方案。在O(n)时间和O(n + k)空间。其中n是数组的大小&amp; k是我们用它来检查可分性的数字。
将数组视为A [n],数字为K
为此,创建一个大小为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;
}