문제
https://www.acmicpc.net/problem/14466
풀이
import java.io.*;
import java.util.*;
public class Main {
static int N, K, R;
static Map<Point, List<Point>> bridge = new HashMap<>();
static ArrayList<Point> cowPoint = new ArrayList<>();
static boolean[][] cow;
static boolean[][] visited;
static int count;
static Queue<Point> queue = new LinkedList<>();
static int totalCowPairs = 0; // 모든 소 쌍의 수
static int connectedPairs = 0; // 서로 연결된 소 쌍의 수
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st;
st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken());
K = Integer.parseInt(st.nextToken());
R = Integer.parseInt(st.nextToken());
cow = new boolean[N][N];
visited = new boolean[N][N];
// Bridge 정보 입력
for (int i = 0; i < R; i++) {
st = new StringTokenizer(br.readLine());
Point startPoint = new Point(Integer.parseInt(st.nextToken()) - 1, Integer.parseInt(st.nextToken()) - 1);
Point endPoint = new Point(Integer.parseInt(st.nextToken()) - 1, Integer.parseInt(st.nextToken()) - 1);
bridge.computeIfAbsent(startPoint, k -> new ArrayList<>()).add(endPoint);
bridge.computeIfAbsent(endPoint, k -> new ArrayList<>()).add(startPoint);
}
// 소의 위치 입력
for (int i = 0; i < K; i++) {
st = new StringTokenizer(br.readLine());
int x = Integer.parseInt(st.nextToken()) - 1;
int y = Integer.parseInt(st.nextToken()) - 1;
cow[x][y] = true;
cowPoint.add(new Point(x, y));
}
// 모든 소 쌍의 수는 K * (K - 1) / 2
totalCowPairs = K * (K - 1) / 2;
// 각 소에서 다른 소까지 BFS 탐색
for (int i = 0; i < cowPoint.size(); i++) {
visited = new boolean[N][N];
queue.add(cowPoint.get(i));
visited[cowPoint.get(i).x][cowPoint.get(i).y] = true;
bfs();
}
// 소가 연결되지 않은 쌍의 수는 totalCowPairs - connectedPairs
int answer = (K*(K-1)/2) - (totalCowPairs - connectedPairs);
System.out.println(answer);
}
// BFS로 소들 간의 연결 상태 탐색
public static void bfs() {
while (!queue.isEmpty()) {
Point point = queue.poll();
int x = point.x;
int y = point.y;
// 현재 위치에서 상하좌우로 이동하며 탐색
explore(x - 1, y, point);
explore(x + 1, y, point);
explore(x, y - 1, point);
explore(x, y + 1, point);
}
}
// 새로운 좌표로 이동 가능한지 확인 후 탐색
public static void explore(int newX, int newY, Point currentPoint) {
if (newX >= 0 && newX < N && newY >= 0 && newY < N && !visited[newX][newY]) {
Point newPoint = new Point(newX, newY);
// 현재 위치에서 새 위치로 가는 다리가 없다면 이동 가능
List<Point> connectedPoints = bridge.getOrDefault(currentPoint, new ArrayList<>());
// 다리가 연결되지 않았으면 이동 가능
if (!connectedPoints.contains(newPoint)) {
visited[newX][newY] = true;
queue.add(newPoint);
// 새로운 위치에 소가 있는지 확인
if (cow[newX][newY]) {
// 소들이 연결된 경우 카운트
connectedPairs++;
}
}
}
}
// Point 클래스 equals, hashCode 메서드 재정의
static class Point {
int x, y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Point point = (Point) o;
return x == point.x && y == point.y;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
}
}
Point라는 점을 먼저 정의했다. 이 점은 equals, hashCode를 오버라이딩 함으로써 값이 같다면 같은 객체로 인식할 수 있게 해두었다.(이렇게 하지 않는다면 소의 위치 점과, 다리의 위치 점의 객체 주소 비교로 인해 동일하게 인식하지 못한다.)
이후 아이디어는 간단한다. 다리의 경우 양쪽 모두 해당하는 조건이므로 다리를 추가할 때 양쪽을 모두 넣어준다.
최종적으로 bfs를 진행하며 소 한마리당 만날 수 있는 소를 찾아간다. 이후, 이 것을 절반으로 나눈 뒤 총 소 쌍에서 빼줌으로써 정답을 구한다.
별 다른 아이디어는 없지만 오버라이딩 등 코딩적으로 좀 귀찮았던 문제같다. 또한, 점을 Map<Point, List<Point>>로 관리하여 하나의 점에 대해 여러가지 점을 보관하는 방법을 사용한 것 정도가 어려웠던 것 같다.
마지막으로, 고등학교 수학시간에도 많이 나왔던 여집합을 구하는 것이 문제의 엄청난 아이디어가 될 수 있음을 항상 주목하자.
'문제 풀이 > 백준' 카테고리의 다른 글
[백준]1043번. 거짓말(JAVA) (1) | 2024.09.19 |
---|---|
[백준] 17182번. 우주 탐사선 (0) | 2024.09.13 |
[백준] 18428번. 감시 피하기(JAVA) (0) | 2024.09.11 |
[백준] 16639번. 괄호 추가하기3(JAVA) (1) | 2024.09.05 |
[백준]29791번. 에르다 노바와 오리진 스킬(JAVA) (0) | 2024.09.04 |