백준 2295 세 수의 합
1. 문제에 대하여 📦
N개의 숫자로 이루어진 집합 U가 있다. 이 집합 U에서 3 개의 수를 골라서 합한 값을
이 d가 U에 포함되어 있는 경우 중에서 d의 최대값을 구하여라
d라고 해보자.이 d가 U에 포함되어 있는 경우 중에서 d의 최대값을 구하여라
2. 코드가 나오기까지 🛠️
KEY WORD: HashMap, 이분탐색세 수의 합을 식으로 나타내면 다음과 같다.
해당 식은 다음과 같이 나타낼 수 있다.
해당 식은 다음과 같이 나타낼 수 있다.
따라서 집합에 나올 수 있는 모든 두 수의 합 (a+b)를 구하여 Map 저장한 뒤
집합에서 나올 수 있는
집합에서 나올 수 있는
d-c 조합을 크기가 큰 순서대로 하나씩 'HashMap 존재하는지' 확인한다. 최초로 존재하는 경우의 d 값이 최대값이므로 그 값을 출력한다.(1) 시간복잡도 분석 ⏳
- 모든 두 수의 합 더하기 =
- 모든 두 수의 뺄셈 확인 = O
두 경우는 연속되지 않으므로, 최악의 시간 복잡도는
3. 코드 🌐
import java.util.*; import java.io.*; public class Main { static int N; static int [] nums; static HashMap<Integer, Boolean> map = new HashMap<>(); public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); N = Integer.parseInt(br.readLine()); nums = new int [N]; for(int i = 0; i < N; i++){ nums[i] = Integer.parseInt(br.readLine()); } Arrays.sort(nums); for(int i = 0; i < N; i++){ for(int j = 0; j < N; j++){ map.put(nums[i]+nums[j], true); } } for(int i = N-1; i >= 0; i--){ for(int c = 0; c < N; c++){ if(map.getOrDefault(nums[i] - nums[c], false)){ System.out.println(nums[i]); return; } } } } }
4. 트러블 슈팅 or 배운 점📝
(1) 투 포인터 + 이분 탐색으로는 풀리지 않을까?
투 포인터로 2 수를 정하고 마지막 하나의 값을 이분 탐색으로 구하는 풀이 또한 생각해보았다. 하지만 해당 풀이는 모든 경우의 수를 고려하지 않기에 틀린다.
모든 경우의 수가 고려되지 않는 이유는, 이분탐색은 '범위 내에 세수의 합을 만족시킬 마지막 멤버 수가 있는지' 만 확인시켜줄 뿐인데, 이를 통해 양 포인터를 움직일 경우, 잘못된 움직임이 존재할 수 있기 때문이다. 또한 역방향 운용 투포인터에서는 중복된 값을 선택해도 된다는 조건을 지키기 어렵다.
import java.util.*; import java.io.*; public class Main { static int N; static int [] nums; public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); N = Integer.parseInt(br.readLine()); nums = new int [N]; for(int i = 0; i < N; i++){ nums[i] = Integer.parseInt(br.readLine()); } Arrays.sort(nums); for(int i = N-1; i >= 3; i--){ if (isComplete(i)) { System.out.println(nums[i]); return; } } } public static boolean isComplete(int target){ int left = 0; int right = target-1; while (left <= right){ int result = binary_search(target,left,right); if(result == 0) return true; else if(result == -1) right--; else left++; } return false; } public static int binary_search(int target, int left, int right){ int start = left; int end = right; while (start <= end){ int mid = (start+end)/2; if(nums[left] + nums[right] + nums[mid] == nums[target]) return 0; else if(nums[left] + nums[right] + nums[mid] > nums[target]) end = mid - 1; else start = mid+1; } if(end <= left) return -1; else return 1; } }
5 1 2 12 17 25
해당 풀이의 답은 12+12+1 = 25 인데,
초반에 이분탐색에서
left = 1 (index = 0), right = 17 (index = 3) 값으로 초기화 된 뒤에mid = 2 (index = 1) 이면, 1+17+2 = 20 (작음 - 상향조정)mid = 12 (index = 2)이면, 1+17+12 = 30 (큼 - 하향 조정)이 되어서 12 + 12 + 1 을 구하는 경우의 수를 놓쳐버린다.