https://www.acmicpc.net/problem/7453
4개의 배열 A,B,C,D가 주어졌을 때 해당 배열들에서 수를 하나씩 선택했을 때 선택한 수들의 합이 0인 경우가 몇가지인지 구하는 문제다.
나이브하게 4중포문으로 구현이 가능하긴 하지만 아무리 제한시간이 12초라도 4000을 네 번 곱하면(N⁴) 시간초과가 당연하다.(N = 1억에 1초로 생각하면 된다)
아이디어는 선형대수학을 떠올렸는데 선형대수학에서 ABx = C의 방정식을 풀때 행렬 AB를 구하는 과정에 O(N³)이 걸리기 때문에 Bx를 치환함으로써 O(N²) 두번으로 문제를 해결했었다.
이 문제도 4중포문 대신 AB의 모든 경우의 수를 계산하고(O(N²)), CD의 모든 경우의 수를 계산해서(O(N²)) AB배열과 CD배열을 투포인터로 접근(O(N))해서 최종 O(N²) 의 시간복잡도로 해결하였다.
#include <iostream>
#include <algorithm>
#define lint long long
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(0);
int n;
int arr[4][4001];
cin >> n;
for(int i = 0; i < n; i++){
for(int j = 0; j < 4; j++){
cin >> arr[j][i];
}
}
const int SIZE = 16000000;
int *ab = new int[SIZE];
int *cd = new int[SIZE];
int len = 0;
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
ab[len] = arr[0][i]+arr[1][j];
cd[len] = arr[2][i]+arr[3][j];
len++;
}
}
sort(ab,ab+len);
sort(cd,cd+len);
int left = 0, right = len-1;
lint cnt = 0;
while(left < len && right >= 0){
if(ab[left]+cd[right] == 0){
int tmpAB = ab[left], tmpCD = cd[right];
lint cntAB = 0, cntCD = 0;
//tmpAB와 같은수의 개수 카운트
while(left < len && tmpAB == ab[left]){
cntAB++;
left++;
}
//tmpCD와 같은수의 개수 카운트
while(right >= 0 && tmpCD == cd[right]){
cntCD++;
right--;
}
cnt += (cntAB*cntCD);
continue;
}
if(ab[left]+cd[right] > 0){
right--;
}
else if(ab[left]+cd[right] < 0){
left++;
}
}
cout << cnt;
}
'📚알고리즘 > 백준' 카테고리의 다른 글
[백준 2655번] 가장높은탑쌓기 (C++) (0) | 2024.07.18 |
---|---|
[백준 31443번] 준영이 (C++) (0) | 2024.07.17 |
[백준 24025번] 돌의 정령 줄세우기 (C++) (0) | 2024.07.16 |
[백준 7561번] 크래머의 공식 (C++) (0) | 2024.07.06 |
[백준 31404번] 아리스, 청소합니다! (Easy) (C++) (0) | 2024.07.03 |