2022. 8. 2. 13:50ㆍ알고리즘/코딩테스트(백준)
문제
한 배열 A[1], A[2], …, A[n]에 대해서, 부 배열은 A[i], A[i+1], …, A[j-1], A[j] (단, 1 ≤ i ≤ j ≤ n)을 말한다. 이러한 부 배열의 합은 A[i]+…+A[j]를 의미한다. 각 원소가 정수인 두 배열 A[1], …, A[n]과 B[1], …, B[m]이 주어졌을 때, A의 부 배열의 합에 B의 부 배열의 합을 더해서 T가 되는 모든 부 배열 쌍의 개수를 구하는 프로그램을 작성하시오.
예를 들어 A = {1, 3, 1, 2}, B = {1, 3, 2}, T=5인 경우, 부 배열 쌍의 개수는 다음의 7가지 경우가 있다.
T(=5) = A[1] + B[1] + B[2]
= A[1] + A[2] + B[1]
= A[2] + B[3]
= A[2] + A[3] + B[1]
= A[3] + B[1] + B[2]
= A[3] + A[4] + B[3]
= A[4] + B[2]
입력
첫째 줄에 T(-1,000,000,000 ≤ T ≤ 1,000,000,000)가 주어진다. 다음 줄에는 n(1 ≤ n ≤ 1,000)이 주어지고, 그 다음 줄에 n개의 정수로 A[1], …, A[n]이 주어진다. 다음 줄에는 m(1 ≤ m ≤ 1,000)이 주어지고, 그 다음 줄에 m개의 정수로 B[1], …, B[m]이 주어진다. 각각의 배열 원소는 절댓값이 1,000,000을 넘지 않는 정수이다.
1. 먼저 배열 두 개를 각각 입력받는다.
2. 반복문을 사용해서 부배열을 생성한다(ArrayList).
3. 투포인터를 사용해 부배열의 합과 목표 합인 T를 비교한다.
문제의 핵심은 투포인터를 사용해서 부배열의 합을 구하는 과정이다.
예시처럼 A = {1, 3, 1, 2}, B = {1, 3, 2}인 경우
A의 부배열은 subA ={1, 3, 1, 2, 4, 4, 3, 5, 6, 7}
B의 부배열은 subB ={1, 3, 2, 4, 5, 6}으로 구할 수 있다.
투포인터 사용을 위해 subA는 오름차순, subB는 내림차순으로 정렬하면
subA ={1, 1, 2, 3, 3, 4, 4, 5, 6, 7}
subB= {6, 5, 4, 3, 2, 1} 로 정렬된다.
이제 ptA, ptB = 0 부터 시작해 두 부배열의 합을 구한다
subA.get(ptA) + subB.get(ptB)
그리고 경우의 수는 세가지로 나뉜다.
- 두 부배열의 합이 T보다 큰경우
- 부배열의 합이 T보다 작은경우
- 부배열의 합이 T인경우
만약 1번 경우처럼 부배열의 합이 T보다 크다면 ? ptB를 1 증가시키면 된다. subB는 내림차순으로 정렬했기 때문
반대로 부배열의 합이 T보다 작다면 오름차순 정렬한 subA의 커서를 1 증가시킨다.
만약 부배열의 합이 T라면 현재 ptA와 ptB가 가리키는 부배열과 같은 원소가 잇달아 몇 번 등장하는지 subA와 subB를 순서대로 뒤지면서 개수를 카운트하면 된다.
제출한 코드에서는 두 부배열의 합 == T를 사용하기보다는
target이라는 변수를 사용했다.
target = T - subA.get(ptA);
결국 subB.get(ptB)를 target으로 치환했다고 생각해도 좋다.
코드를 보면 이해할 수 있을 것이다.
제출한 코드
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.*;
public class Main {
static long T;
static int N, M;
static long[] inputA, inputB;
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
T = Long.parseLong(br.readLine());
N = Integer.parseInt(br.readLine());
inputA = new long[N];
StringTokenizer st = new StringTokenizer(br.readLine());
for (int i = 0; i < N; i++) {
inputA[i] = Long.parseLong(st.nextToken());
}
M = Integer.parseInt(br.readLine());
inputB = new long[M];
st = new StringTokenizer(br.readLine());
for (int i = 0; i < M; i++) {
inputB[i] = Long.parseLong(st.nextToken());
}
List<Long> subA = new ArrayList<>();
List<Long> subB = new ArrayList<>();
//subA 구하기
for (int i = 0; i < N; i++) {
long sum = inputA[i];
subA.add(sum);
for (int j = i + 1; j < N; j++) {
sum += inputA[j];
subA.add(sum);
}
}
//subB 구하기
for (int i = 0; i < M; i++) {
long sum = 0;
for (int j = i; j < M; j++) {
sum += inputB[j];
subB.add(sum);
}
}
//subA, subB 정렬하기
Collections.sort(subA);
Collections.sort(subB, Comparator.reverseOrder());
long result = 0;
int ptA = 0;
int ptB = 0;
while (true) {
long currentA = subA.get(ptA);
long target = T - currentA;
//currentB == target -> subA, subB 같은 수 개수 체크 -> 답구하기. ptA+ ptB+
if (subB.get(ptB) == target) {
long countA = 0;
long countB = 0;
while (ptA < subA.size() && subA.get(ptA) == currentA){
countA++;
ptA++;
}
while (ptB < subB.size() && subB.get(ptB) == target){
countB++;
ptB++;
}
result += countA * countB;
}
//currentB > target -> ptB++
else if (subB.get(ptB) > target) {
ptB++;
}
//currentB < target -> ptA++
else {
ptA++;
}
//탈출 조건
if (ptA == subA.size() || ptB == subB.size()) {
break;
}
}
}
}
'알고리즘 > 코딩테스트(백준)' 카테고리의 다른 글
백준 1238: 파티 [Java] - 포포 (1) | 2022.09.11 |
---|---|
백준 1463: 1로 만들기 [Java] -포포 (0) | 2022.09.01 |
백준 3055: 탈출 [Java] - 포포 (0) | 2022.08.02 |
백준 1759: 암호 만들기 [Java] - 포포 (0) | 2022.07.07 |
백준 15663: N과 M (9) (0) | 2022.07.05 |