문제
개의 서로 다른 양의 정수 , , ..., 으로 이루어진 수열이 있다. 의 값은 1보다 크거나 같고, 1,000,000보다 작거나 같은 자연수이다. 자연수 가 주어졌을 때, 을 만족하는 쌍의 수를 구하는 프로그램을 작성하시오.
입력
첫째 줄에 수열의 크기 n이 주어진다. 다음 줄에는 수열에 포함되는 수가 주어진다. 셋째 줄에는 x가 주어진다. (1 ≤ n ≤ 100000, 1 ≤ x ≤ 2000000)
출력
문제의 조건을 만족하는 쌍의 개수를 출력한다.
풀이
서로 다른 두 수의 합이 x가 되는 쌍의 개수를 세는 문제다. 정렬한 뒤 양끝에서 좁혀 오는 투 포인터를 쓰면 중복 없이 모든 후보를 효율적으로 확인할 수 있다.
현재 합이 x보다 작으면 더 큰 값을 만들기 위해 왼쪽 포인터를 올리고, 크면 오른쪽 포인터를 내린다. 정확히 x가 되면 정답 개수를 늘린 뒤 두 포인터를 모두 움직이면 된다.
정렬만 끝나면 합의 대소 비교로 포인터 이동 방향이 바로 정해진다. 완전탐색으로 두 수를 모두 고르지 않아도 된다는 점이 핵심이다.
코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;
public class Main {
private static final StringBuilder sb = new StringBuilder();
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine());
int[] arr = new int[n];
StringTokenizer st = new StringTokenizer(br.readLine());
for (int i = 0; i < n; i++)
arr[i] = Integer.parseInt(st.nextToken());
int x = Integer.parseInt(br.readLine());
solve(n, arr, x);
System.out.println(sb);
}
public static void solve(int n, int[] arr, int x) {
Arrays.sort(arr);
int count = 0;
int left = 0;
int right = n - 1;
while (right > left) {
int sum = arr[left] + arr[right];
if (sum == x) {
count++;
left++;
right--;
} else if (sum > x) {
right--;
} else {
left++;
}
}
sb.append(count);
}
}복잡도
- 시간 복잡도: 정렬 후 투 포인터로 한 번 훑으므로 이다.
- 공간 복잡도: 입력 배열을 저장하므로 이다.
마무리
정렬 후 양끝 포인터를 좁혀 가면 합이 x인 쌍을 중복 없이 빠르게 셀 수 있다.
