본문으로 바로가기

[백준 17472] 다리만들기2 -JAVA //le_effort

category 알고리즘 2020. 6. 6. 18:38

다리 만들기 2

시간 제한메모리 제한제출정답맞은 사람정답 비율
1 초512 MB75022377131227.063%

문제

섬으로 이루어진 나라가 있고, 모든 섬을 다리로 연결하려고 한다. 이 나라의 지도는 N×M 크기의 이차원 격자로 나타낼 수 있고, 격자의 각 칸은 땅이거나 바다이다.

섬은 연결된 땅이 상하좌우로 붙어있는 덩어리를 말하고, 아래 그림은 네 개의 섬으로 이루어진 나라이다. 색칠되어있는 칸은 땅이다.

img

다리는 바다에만 건설할 수 있고, 다리의 길이는 다리가 격자에서 차지하는 칸의 수이다. 다리를 연결해서 모든 섬을 연결하려고 한다. 섬 A에서 다리를 통해 섬 B로 갈 수 있을 때, 섬 A와 B를 연결되었다고 한다. 다리의 양 끝은 섬과 인접한 바다 위에 있어야 하고, 한 다리의 방향이 중간에 바뀌면 안된다. 또, 다리의 길이는 2 이상이어야 한다.

다리의 방향이 중간에 바뀌면 안되기 때문에, 다리의 방향은 가로 또는 세로가 될 수 밖에 없다. 방향이 가로인 다리는 다리의 양 끝이 가로 방향으로 섬과 인접해야 하고, 방향이 세로인 다리는 다리의 양 끝이 세로 방향으로 섬과 인접해야 한다.

섬 A와 B를 연결하는 다리가 중간에 섬 C와 인접한 바다를 지나가는 경우에 섬 C는 A, B와 연결되어있는 것이 아니다.

아래 그림은 섬을 모두 연결하는 올바른 2가지 방법이고, 다리는 회색으로 색칠되어 있다. 섬은 정수, 다리는 알파벳 대문자로 구분했다.

imgimg
다리의 총 길이: 13D는 2와 4를 연결하는 다리이고, 3과는 연결되어 있지 않다.다리의 총 길이: 9 (최소)

다음은 올바르지 않은 3가지 방법이다

imgimgimg
C의 방향이 중간에 바뀌었다D의 길이가 1이다.가로 다리인 A가 1과 가로로 연결되어 있지 않다.

다리가 교차하는 경우가 있을 수도 있다. 교차하는 다리의 길이를 계산할 때는 각 칸이 각 다리의 길이에 모두 포함되어야 한다. 아래는 다리가 교차하는 경우와 기타 다른 경우에 대한 2가지 예시이다.

imgimg
A의 길이는 4이고, B의 길이도 4이다.총 다리의 총 길이: 4 + 4 + 2 = 10다리 A: 2와 3을 연결 (길이 2)다리 B: 3과 4를 연결 (길이 3)다리 C: 2와 5를 연결 (길이 5)다리 D: 1과 2를 연결 (길이 2)총 길이: 12

나라의 정보가 주어졌을 때, 모든 섬을 연결하는 다리 길이의 최솟값을 구해보자.

입력

첫째 줄에 지도의 세로 크기 N과 가로 크기 M이 주어진다. 둘째 줄부터 N개의 줄에 지도의 정보가 주어진다. 각 줄은 M개의 수로 이루어져 있으며, 수는 0 또는 1이다. 0은 바다, 1은 땅을 의미한다.

출력

모든 섬을 연결하는 다리 길이의 최솟값을 출력한다. 모든 섬을 연결하는 것이 불가능하면 -1을 출력한다.

제한

  • 1 ≤ N, M ≤ 10
  • 3 ≤ N×M ≤ 100
  • 2 ≤ 섬의 개수 ≤ 6

예제 입력 1 복사

예제 출력 1 복사

예제 입력 2 복사

예제 출력 2 복사

예제 입력 3 복사

예제 출력 3 복사

예제 입력 4 복사

예제 출력 4 복사

 

풀이

크루스칼 알고리즘을 모른다면 풀 수 없는 문제.

되게 여러가지 개념이 들어가서 풀 때 재밌었던 문제다.

좋은 문제인거 같다

 

1. 맵 구역을 나누기

각각의 구역에 번호를 붙혀준다.

dfs, bfs 어떤걸로 붙혀도 상관없지만 나는 dfs로 돌리면서

각 구역에 해당하는 좌표와 숫자를 전역변수로 선언한 q에다가 넣어줬다

그 이유는 아래에 나온다.

 

우선 x,y좌표 그리고 섬의 번호를 붙히기 위해서 Node 클래스를 만들었다.

 

 

위의 4개가 해당하는 코드이다.

왜 dfs를 진행하면서 q에 넣어줬냐면 문제에서 각각의 섬에서 다리를 놓을 때

해당하는 섬의 모든 인덱스를 돌면서 다리를 놔야한다.

(1번섬이 2x2 크기고 인덱스가 0,0 0,1 1,0 1,1 일 경우 4개의 좌표에서 다 다리를 놓아봐야 한다)

 

2. 각각의 섬에서 다리설치 해보기

여기서 조금 애를 먹었던것 같다.

큐에 담겨져있는걸 4방향으로 해보면서 다리를 놓아보면 된다.

다리를 놓을수 있다면 우선순위 큐에 넣어준다

크루스칼 알고리즘을 위해 우선순위 큐를 만들어 준다.

왜 크루스칼인지는 다음 단계에서 설명하겠다.

 

 

 

 

내가 애를 먹었던건 cnt의 길이를 구하는 것 인데

햇갈렸던게 맨처음엔

초기상태에서 좌표를 하나씩 증가시키고 cnt도 올려주었다

이게 무슨말이냐면

x= x+dx[d];

y=y+dy[d];

cnt++;

이렇게 했는데 이렇게하면 카운트가 1개 더 나오는 오류가 나왔었다

말로 어떻게 풀어야 할 지 잘 모르겠는데, 무튼 저렇게 하면 된다...

 

 

3. 크루스칼 알고리즘

지금 우리는 정점과 간선을 가지고 있다.

다리는 정점과 정점을 잇는 가중치를 가진 간선이고

각각의 섬들은 정점이다.

이 때 이를 최소한으로 연결하기 위한 MST 즉 크루스칼이 떠오른다.

 

4. 섬이 다 연결 됐는지 확인

크루스칼 알고리즘은 주어진 정점을 다 연결하는 최소신장스패닝트리이다.

즉 n개의 정점이 있다면 n-1개의 간선을 가지고 있어야 한다.

이제 크루스칼 알고리즘을 하면서 몇개의 간선을 놓을수 있는지 체크 하는데

n-1개의 간선을 가지고 있어야 한다

island_num-2 라서 오해 할 수 있는데

내가짠 로직에선 island_num은 섬의 개수보다 1개가 더 나와서 저렇게 한 거지

사실상 섬의 개수 -1 개의 간선이 연결 됐는지 확인하는 것이다.

 

전체코드

 

후기

되게 재밌었던 문제였다 풀고나서 뿌듯한 문제였다.

여러가지 알고리즘이 복합적으로 쓰인 문제를 풀 땐 다 풀고나면 뿌듯하다.