-
문제 링크
-
사용 언어
C++
-
소스
#include <iostream> #include <algorithm> #define abs(x) (((x)<0)?((-1)*(x)):(x)) using namespace std; int N; int A[100000]; int Min = ~(1 << 31); bool compare(int a, int b){ return abs(a) < abs(b); } int main() { cin >> N; for(int i = 0; i < N; i++){ cin >> A[i]; } sort(A, A+N, compare); for(int i = 1; i < N; i++){ int x = A[i-1]+A[i]; if(abs(x) < abs(Min)) Min = x; } cout << Min; }
-
논리
만약 모든 원소의 쌍의 합을 조사한다면, 시간 복잡도는 O(N2)이 된다.
그리고 이러한 알고리즘은 시간초과 에러를 띄우게 된다.
더 좋은 알고리즘은 다음과 같다.
{Ai}를 절댓값 기준으로 정렬한 후, 각 이웃끼리 합의 절댓값의 최솟값을 찾는다.
어떻게 절댓값 기준으로 정렬된 배열의 이웃에서 최솟값이 발생할 것이라고 보장할 수 있는가.
증명과정을 보이도록 하겠다.
-
def
{Ci} = c1, c2, …, cn ({Ai}를 절댓값 기준으로 오름차순 정렬한 배열)
-
claim
\[\exists c_i, c_j \quad s.t \quad |c_i + c_j| \mbox{ is minimum} \quad \Rightarrow \quad |i-j|=1\] -
proof
귀류법을 통해 증명하기 위해 다음과 같이 결론을 부정해본다.
\[|c_i + c_j| \mbox{ is minimum} \quad \land \quad |i - j| > 1\]특히 일반성을 잃지 않고 \(i < j\) 라고 둘 수 있다.
그런데 다음 3가지 중 적어도 하나는 참이다.
- \[c_i * c_j > 0 \quad \land \quad [\; \exists c_k \quad s.t \quad i < k < j \quad \land \quad c_i * c_k > 0 \;]\]
- \[c_i * c_j > 0 \quad \land \quad [\; \exists c_k \quad s.t \quad i < k < j \quad \land \quad c_i * c_k < 0 \;]\]
- \[c_i * c_j < 0\]
그러면 어떤 경우라도 가정에 모순이 생긴다. 그 이유를 보이겠다.
-
다음 사실을 활용하자.
\[xy \ge 0 \quad \Rightarrow \quad |x+y| =|x| + |y|\]그러면
\[\left| c_i + c_j\right|=\left| c_i \right| + \left| c_k \right| < \left| c_i \right| + \left| c_j \right| = \left| c_i + c_j \right|\]이므로 모순
-
삼각부등식을 활용하자.
\[|x+y| \le |x| + |y|\]그러면
\[\begin{cases} |c_i + c_k| \le |c_i| + |c_k| \\\\ |c_i + c_j| \le |c_i| + |c_j| \end{cases}\]이다.
\[\therefore |c_i + c_k| \le |c_i| + |c_k| < |c_i| + |c_j| = |c_i + c_j|\]따라서 모순.
-
다음 사실을 활용하자.
\[\forall c_k(i<k<j), \quad c_i * c_k < 0 \quad \lor \quad c_k * c_j < 0\]일반성을 잃지 않고, \(c_i * c_k < 0\) 이라고 가정할 수 있다.
\[xy < 0 \quad \Rightarrow \quad |x + y| = ||x| - |y||\]임을 활용하자.
특히
\[|c_i| < |c_k| < |c_j|\]이고,
\[\begin{cases} |c_i + c_k| = |c_k| - |c_i|\\ |c_i + c_j| = |c_j| - |c_i| \end{cases}\]이므로
\[\therefore |c_i + c_k| = |c_k| - |c_i| < |c_j| - |c_i| = |c_i + c_j|\]즉, 모순이다.
결국 모든 경우에 대해 모순을 보였으므로, 증명이 완료 된다.
그래서 위와 같은 알고리즘으로 최솟값을 찾아낼 수 있다.
시간 복잡도는 배열을 정렬하는데 O(NlogN)이 걸리고,
정렬된 배열에서 최소값을 찾는데는 O(N)이 걸리므로 O(NlogN) + O(N) = O(NlogN)이 걸린다.
-