https://www.acmicpc.net/problem/7453
7453번: 합이 0인 네 정수
문제 정수로 이루어진 크기가 같은 배열 A, B, C, D가 있다. A[a], B[b], C[c], D[d]의 합이 0인 (a, b, c, d) 쌍의 개수를 구하는 프로그램을 작성하시오. 입력 첫째 줄에 배열의 크기 n (1 ≤ n ≤ 4000)이 주어진다. 다음 n개 줄에는 A, B, C, D에 포함되는 정수가 공백으로 구분되어져서 주어진다. 배열에 들어있는 정수의 절댓값은 최대 228이다. 출력 합이 0이 되는 쌍의 개수를 출력한다. 예제 입력 1 복
www.acmicpc.net
단순히 모든 경우의 수를 고려해서 풀어도 문제는 없으나
일단 문제를 보면 A + B + C + D = 0을 만족하는 모든 경우의 수를 찾아야 합니다. 하지만 무작정 모든 경우의 수를 확인할 수는 없기 때문에 먼저 A + B와 C + D로 나눠서 접근해 보았습니다. A + B와 C + D의 모든 경우의 수를 구해서 따로 저장해두면 4개의 배열에서 2개의 배열을 비교하는 문제가 됩니다. 새롭게 만들어진 2개의 배열을 다시 비교해서 둘을 더했을 때 0이 나오는 경우를 찾아주면 되는 것이죠.
하지만 문제는 이 2개의 배열을 비교하는 부분입니다. 단순히 2중 반복문을 사용해서 비교를 하게 되면 시간 복잡도가 다시
전체적인 알고리즘은 다음과 같습니다.
- A + B, C + D 결과를 따로 계산해서 키는 연산 결과, 값은 그 수의 개수로 각각 해시 테이블에 저장
- A + B 해시 테이블에서 키를 하나씩 꺼내서 음수를 취한 뒤, C + D 해시 테이블에 그 값이 있는지 확인 (예를 들어 A + B 해시 테이블에서 30을 꺼낸 뒤 -30으로 바꾸고 C + D에 -30이 있나 확인)
- 만약 존재한다면 해당 키에 대응되는 값(그 키의 개수)을 서로 곱한 뒤 개수를 셈 (예를 들어 A + B 해시 테이블에 30이 3개, C + D 해시 테이블이 -30이 5개가 있는 경우 0이 되는 경우의 수는 모두 15개가 있으므로 이 둘을 곱한 15를 셈)
- 위 2 ~ 3 과정을 계속 반복해서 합이 0인 모든 경우의 수를 구하고 출력
주의할 점이 있다면 정답이 int 범위를 벗어날 수 있기 때문에 적절한 자료형을 사용하셔야 합니다. 처음에는 C++ STL의 unordered_map을 이용해서 구현했는데 오버헤드가 심해서 그런지 시간 초과를 받았습니다. C#의 Dictionary를 사용했더니 바로 통과가 되더군요. 아무래도 해시 테이블로 짜실 분들은 직접 구현하는 것이 더 좋을 것 같습니다.
using System;
using System.Collections.Generic;
class Program
{
static void Main(string[] args)
{
int n = int.Parse(Console.ReadLine());
ulong answer = 0;
int[] arrA = new int[n];
int[] arrB = new int[n];
int[] arrC = new int[n];
int[] arrD = new int[n];
// 배열 데이터 입력
for (int i = 0; i < n; i++)
{
string[] strArr;
strArr = Console.ReadLine().Split();
arrA[i] = int.Parse(strArr[0]);
arrB[i] = int.Parse(strArr[1]);
arrC[i] = int.Parse(strArr[2]);
arrD[i] = int.Parse(strArr[3]);
}
Dictionary<int, ulong> dictAB = new Dictionary<int, ulong>();
Dictionary<int, ulong> dictCD = new Dictionary<int, ulong>();
// A + B, C + D 결과의 개수를 계산해서 따로 보관
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
int resultAB = arrA[i] + arrB[j];
int resultCD = arrC[i] + arrD[j];
if (dictAB.ContainsKey(resultAB))
{
dictAB[resultAB]++;
}
else
{
dictAB.Add(resultAB, 1);
}
if (dictCD.ContainsKey(resultCD))
{
dictCD[resultCD]++;
}
else
{
dictCD.Add(resultCD, 1);
}
}
}
// (A + B) == (C + D) 경우를 모두 찾고 개수를 계산
foreach (int resultAB in dictAB.Keys)
{
// (C + D) 리스트에 -(A + B) 값이 존재하는지 확인
if (dictCD.ContainsKey(-resultAB))
{
answer += dictAB[resultAB] * dictCD[-resultAB];
}
}
Console.WriteLine(answer);
}
}