https://www.acmicpc.net/problem/1920
1920번: 수 찾기
첫째 줄에 자연수 N(1 ≤ N ≤ 100,000)이 주어진다. 다음 줄에는 N개의 정수 A[1], A[2], …, A[N]이 주어진다. 다음 줄에는 M(1 ≤ M ≤ 100,000)이 주어진다. 다음 줄에는 M개의 수들이 주어지는데, 이 수들
www.acmicpc.net
가장먼저 떠오른 방법
:
"이 안에 X라는 정수가 존재하는지 알아내라" 라는 키워드에서
algorithm 라이브러리의 (find(A.begin(), A.end(), num) 함수가 떠올랐다.
find() 함수는 첫 번째 인자로 벡터의 시작점,
두 번째 인자로 벡터의 종점,
세 번째 인자로 '찾고자 하는 정수'(여기서는 num)를 넣어준다.
반환은 num을 찾았으면 해당 원소의 반복자를 반환하고,
찾지 못했으면 해당 범위의 마지막 end()반복자를 반환한다.
(반복자란 ? : https://eehoeskrap.tistory.com/263 참고)
따라서 find함수를 이용하여 1 과 0 을 출력면 된다고 생각했는데...
시간초과가 떴다.
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main(int argc, char** argv)
{
ios_base::sync_with_stdio(0); cin.tie(0); // 시간초과방지
int n;
scanf("%d", &n);
vector<int> A(n);
for (int i = 0; i < n; i++)
scanf("%d", &A[i]);
int m;
scanf("%d", &m);
for (int i = 0; i < m; i++) {
int num;
scanf("%d", &num);
if (find(A.begin(), A.end(), num) != A.end()) // 찾았다면 1 출력
printf("1\n");
else
printf("0\n");
}
return 0;
}
다르게 접근해본 방법
:
cin 과 cout 때문에 시간초과가 났나,
cout 에서 endl -> \n로 바꾸지 않아 시간초과가 났나,
이 두 방법을 건드렸는데도 해결되지 않아 접근법을 다시 고민해보았다.
하지만 도저히 어떻게 접근해야하는지 아이디어가 떠오르지 않아 구글링 한 결과,
이진탐색으로 푸는 문제였다.
이진 탐색 은 배열의 중간(mid)을 기준으로 데이터를 탐색하기 때문에
배열 내부의 데이터가 정렬되어 있어야만 사용할 수 있는 알고리즘 이므로
일단 이진탐색을 수행하기에 앞서 오름차순 정렬을 해주었다.
작년 하반기 알고리즘 전공 수업에서 배운 이진 탐색 기억을 떠올리면서,
구글링 한 결과 이진탐색의 개념을 간단하게 설명하자면
"변수 3개( start, end, mid ) 를 사용하여 검색범위를 줄여나가면서 원하는 데이터를 검색하는 알고리즘" 이다.
이진 탐색을 사용하면 시간복잡도(O(logN))가 상대적으로 빠르기 때문에
내가 기존에 짰던 'find 함수를 이용하여 벡터의 처음부터 끝까지 전수조사하는 O(N) 방법'보다
더 빠르게 원하는 원소를 검색할 수 있다.
이 방법이라면 시간초과가 나지 않을 것이라는 생각이 들었고,
이진탐색 개념과 설명을 다시 상기시키고 구글링해가며 코드를 다시 짰다.
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <vector>
#include <cmath> //C++
#include <algorithm>
#include <stack>
using namespace std;
int n, m; // 전역으로 선언하여 main 외 함수에도 사용 할 수 있도록 함
vector<int> A;
int binarySearch(int k);
int main(int argc, char** argv)
{
ios_base::sync_with_stdio(0); cin.tie(0); // 시간초과방지
scanf("%d", &n);
for (int i = 0; i < n; i++) {
int tmp;
scanf("%d", &tmp);
A.push_back(tmp);
}
sort(A.begin(), A.end()); // 오름차순 정렬
scanf("%d", &m);
// 오름차순 정렬 후 이진탐색 진행
for (int i = 0; i < m; i++) {
int num;
scanf("%d", &num);
int result = binarySearch(num);
printf("%d\n", result);
}
return 0;
}
int binarySearch(int k) {
int l = 0; // 이진탐색에서 맨 왼쪽 인덱스를 기억할 변수
int r = n - 1; // 이진탐색에서 맨 오른쪽 인덱스를 기억할 변수
int m;
while (r >= l) {
m = (l + r) / 2; // 이진탐색에서 가운데(key, 즉 k)를 기억할 변수, 매번 찾는값이 k가 맞는지 검증
if (A[m] == k)
return 1;
else if (A[m] > k)
r = m - 1;
else if (A[m] < k)
l = m + 1;
}
return 0;
}
이진탐색에 관한 자세한 건 주석으로 설명을 달아놓았다.
내가 쓴 방법은 main 함수에서 binarySearch(이진탐색함수)의 반환값(1또는 0)을 받아
받은 반환값을 출력하는 방법이었지만,
binarySearch(이진탐색함수)함수 안에서 직접적으로 출력하는,
즉 binarySearch()의 반환값을 void로 지정하고
그냥 그 함수 안에서 찾으면1, 못찾으면 0을 출력하는 방법도 괜찮을 것 같다.
깨달은 점
:
내가 떠올린 방법의 시간복잡도가 정말 최선인지 고려해보자.
아무래도 벡터의 전 범위를 도는 것은 오래걸릴 수 밖에 없다.
찾고자 하는 원소가 특정할때 이진탐색을 사용하면,
시간복잡도를 O(logN)로 줄일 수 있고, 백준 시간초과를 방지할 수 있다.
'코딩테스트 공부' 카테고리의 다른 글
[c++]백준1158번:요세푸스 문제 (0) | 2023.09.10 |
---|---|
[c++]백준1427번:소트인사이드 (0) | 2023.09.03 |
[c++]백준11098번:첼시를 도와줘! (0) | 2023.08.27 |
[c++] 벡터 (0) | 2023.07.08 |