2013-10-23 1 views
3

하나의 알고리즘 질문에 갇혀 있습니다. 아래의 문제에 대한 효율적인 알고리즘을 제안 해주십시오.주어진 숫자로 나누어 진 배열의 부분 배열 수를 찾으십시오

질문 합 주어진 숫자로 나누어 하위 어레이의

찾기 번호입니다.

I 복잡성이다 O 한 알고리즘 (N^2) 배열, 여기서, N = 크기가 만든

직장 내.

내 코드 주어진 수 X를 들어

#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

작성한 코드의 문제점은 무엇입니까? –

+1

당신의 솔루션은 가능한 많은 조합을 건너 뛴다 고 생각합니다. 인접한 요소의 합계를 여기에서 확인하십시오 (문제에서 "하위 배열"이 정의되지 않은 한). – Ashalynd

+1

@Ahalynd "하위 배열"은 일반적으로 배열 (예 : [최대 부분 배열 문제] (http://en.wikipedia.org/wiki/Maximum_subarray_problem)). 비 연속적인 부분의 경우 일반적으로 "부분 집합"(예 : [부분 집합 합계 문제] (http://en.wikipedia.org/wiki/Subset_sum_problem))에 대해 이야기합니다. – Dukeling

답변

21

...

기본 개념 :

하는 경우 (정확성의 비공식적 증명) 숫자의 합 [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 

그래서 : 오른쪽에 그 금액이 모두 X로 나눈 같은 나머지가있는 경우

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으로 나눌 수 있습니다. 정교 :

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 

이제 우리는 0 <= Q, S < X으로 일부 정수 P, Q, RS에 대한 양식 RX + S에 양식 PX + QAB을 변환 할 수 있습니다. 여기서, 정의에 의해, 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은 (결과가 단순히 (P-R)입니다) X로 나눌 수 있습니다. 이제 Q - SX으로 나눌 수 있어야합니다. 그러나 0 <= Q, S < X부터 동일해야합니다.

예 :

B = 13, A = 7, X = 3을 보자.

여기 B % X = 1A % X = 1.

B4*3 + 1A2*3 + 1으로 다시 쓸 수 있습니다.

3으로 나눌 수 있습니다.

높은 수준의 접근 방식 :

각 값은 배열의 처음부터 시작까지 추가 일부 지정된 위치에 끝낼 수있는 방법을 여러 가지 방법으로 표현 key -> value의 해시 맵을 구축 sum mod X = key (아래 예에서 "Mod 3"라인 및 맵 값 참조).

지금, 위의 논리에 따라, 우리는 두 개의 하위 어레이는 처음부터 시작하여 모두 같은 sum mod X을 가진 각각의 위치 ab에서 끝나는 경우, 부분 배열 [a, b]X으로 나눌 수 것이라는 점을 알고있다.

따라서 해시지도의 각 값은 가능한 시작 및 끝점 집합의 크기를 나타내며 X으로 나눌 수있는 부분 배열을 제공합니다 (모든 점은 시작 또는 끝 점이 될 수 있음).

시작 지점과 종료 지점을 선택할 수있는 방법의 수는 단순히
value choose 2 = value!/(2*(value-2)!) (값이 1이면 0)입니다.

따라서 해시 맵의 각 값에 대해 계산하여 모두를 더해서 X으로 나눌 수있는 하위 배열 수를 얻습니다.

알고리즘 :

지금까지 mod X 그 나머지 값이 표시되는 빈도의 수에 매핑 된 모든 숫자의 누적 합계를 저장하는 해시 맵을 구축는 (예상 O(n)으로 구성).

0의 값을 1 씩 늘립니다.이 값은 배열의 시작에 해당합니다.

카운트에 value!/(2*(value-2)!) 추가 해시 맵의 각 값에 대해 0

에 카운트를 초기화한다.

숫자가 원하는 값입니다.

실행 시간 :

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 
+0

질문에 방금 하위 배열 형식이 아닌 부분 시퀀스 형식으로 읽은 여러 하위 배열에 대한 질문이있는 동안 연속 요소를 고려했습니다. 예를 들어 3 + 2 + 1 또는 0 + 3 + 2 + 1을 사용하지 않은 경우, 그런데 명령을 유지하는 것이 중요한지 아닌지를 모르는 사이에, 예를 들어 3 + 2 + 1, 6 번 또는 단 한번 계산해야한다는 것을 모릅니다. (나는 후자의 경우를 받아 들일 수 있다고 생각한다). –

+2

@SaeedAmiri "하위 배열"은 대개 배열의 연속 된 부분을 나타냅니다. [최대 부분 배열 문제] (http://en.wikipedia.org/wiki/Maximum_subarray_problem). – Dukeling

+0

아니요, 예를 들어 문제가 생겼습니다 * 문구가 * "인접한 하위 배열"*이라는 문구가 매우 명확하게 명시되어 있습니다. 문제 이름은 일반적이지만 (짧아야하므로 문제의 이름으로 모든 세부 사항을 쓸 필요는 없습니다.). 그리고 여기서 문제 정의에서, 연속성에 대한 논쟁은 없습니다. 그래서 우리는 그것이 계속되지 않는다고 가정해야합니다. –

2

내가 쉽게 솔루션을 가질 수있다. O (n) 시간 및 O (n + k) 공간. 여기서 n은 배열의 크기입니다. & k는 우리가 나누기를 검사하는 숫자입니다.

는 A [N] 등의 배열을 고려 수가 K

  1. 다른 배열 SUM_TILL_NOW [N]을 생성한다. 각 A [i] 채우기 SUM_TILL_NOW [i] = SUM_TILL_NOW [i-1] + A [i] % K에 대해
  2. ; (SUM_TILL_NOW [0] = A [0])
  3. 이 새로운 배열에서 두 개의 숫자를 찾습니다.

는이 SUM_TILL_NOW 어레이 위에 새로운 배열 CHECK [] 크기 K.

반복 처리를 만들고 체크 [SUM_TILL_NOW는 [내가] 설정되었는지 확인 수행.

i로 설정하지 않은 경우

다른 검사 [SUM_TILL_NOW은 [I], 나 합 이하 동일의 C++ 함수

K.

로 나누어 서브 세트이다.

#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; 
} 
관련 문제