https://www.acmicpc.net/problem/17472
17472번: 다리 만들기 2
첫째 줄에 지도의 세로 크기 N과 가로 크기 M이 주어진다. 둘째 줄부터 N개의 줄에 지도의 정보가 주어진다. 각 줄은 M개의 수로 이루어져 있으며, 수는 0 또는 1이다. 0은 바다, 1은 땅을 의미한다.
www.acmicpc.net
MST 복습문제 - 삼성 A형 기출문제
접근 방법
최대 10*10의 격자칸에 섬이 존재하고, 섬을 다리로 잇는 최소 비용을 구한다.
문제를 보았을때 접근 방법은 누구나 비슷하게 쉽게 떠올릴 수 있을 것 같고, 구현이 조금 많이 어려웠다.
1. 각 섬에 dfs/bfs로 번호를 붙인다.
2. 각 섬에서 섬으로 이동하는 dist를 구한다.
3. MST를 계산한다.
- 각 섬에 번호를 붙이는것은 백준에 같은 문제가 있고, 너무 기초 dfs/bfs문제니까 자세히 설명하지 않는다.
- 섬과 섬 간의 거리를 구하는 방법은 다음과 같다.
하나의 섬에서, 해당 섬의 모든 좌표에서 4방향으로 나아가며 다른 섬을 만나면 2차원 matrix에 저장한다.
그리고 하나의 섬이 갖는 좌표를 1번 단계에서 벡터에 저장하면 좀 더 간편하다.
>더 나은 방법으로, 그냥 N*N배열에서 시작하는 것이 좀 더 편할 것 같다.
나는 위의 방법을 처음에 떠올렸으나, 조금 다른 방식으로 계산했다가 처참히 실패했다.
A섬과 B섬의 모든 좌표를 각각 a*b 개 만큼 대응하여 x축 또는 y축이 같은것이 있다면 동일선 상에 존재하므로 차이를 dist로 저장하고, dist의 최솟값을 구했다. 그러나 이는 ㄷ자같은 섬이 존재하거나 섬의 모양이 조금만 불규칙해도 안된다. - MST를 구하는 방법은 이전 포스팅에 잘 나와있다.
추가로 모든 섬이 연결되었는지 확인해야하므로, 간선이 (섬의 개수-1)가 맞는지 확인하면 된다.
수 많은 알고리즘과 테크닉이 결합된 문제로, 풀이를 떠올리는 건 어렵지 않았으나 구현에서 시간이 너무 오래 걸렸다.
그리고 최대 격자가 100칸밖에 안되므로 시간복잡도도 거의 고려하지 않아도 된다.
구현 문제 연습좀 해야겠다.!
풀이
tag : MST, dfs, bfs, bruteforce, implement
#include<iostream>
#include<vector>
#include<string.h>
#include<algorithm>
#define INF 0x3f3f3f3f
#define pii pair<int,int>
#define mp make_pair
using namespace std;
const int dy[4] = {-1, 0, 1, 0};
const int dx[4] = {0, 1, 0, -1};
int N, M;
int arr[11][11];
int visited[11][11] = {0};
int idx = 1; //섬의 idx
vector<pii> coords[7]; //6개 섬 구성좌표
vector<pair<int, pii>> edges; //거리, (idx1 idx2)
int distArr[7][7];
int parent[7] = {0, 1, 2, 3, 4, 5, 6};
void dfs(int r,int c){
coords[idx].push_back(mp(r, c));
visited[r][c] = 1;
arr[r][c] = idx;
for (int i = 0; i < 4;i++){
int nr = r + dy[i];
int nc = c + dx[i];
if(nr<0 || nc<0 || nr>=N || nc>=M)
continue;
if(arr[nr][nc]==1 && visited[nr][nc]==0)
dfs(nr, nc);
}
}
void calcDist(int idx){
/* idx섬부터 뻗어나가는 최소거리 구하기*/
for (int i = 0; i < coords[idx].size();i++){
pii coord = coords[idx][i];
for (int j = 0; j < 4;j++){
//4방향쭉쭉
int dist = 1;
while(1){
int nr = coord.first + dy[j]*dist;
int nc = coord.second + dx[j]*dist;
if(nr<0 || nc<0 || nr>=N || nc>=M || arr[nr][nc]==idx)
break;
if(arr[nr][nc]!=0){//섬 발견
if(dist==2) //거리가1
break;
distArr[idx][arr[nr][nc]] = distArr[arr[nr][nc]][idx] = min(distArr[idx][arr[nr][nc]],dist - 1);
break;
}
dist++;
}
}
}
}
int findParent(int idx){
if(parent[idx]==idx)
return idx;
return findParent(parent[idx]);
}
void unionParent(int idx1,int idx2){
int pr1 = findParent(idx1);
int pr2 = findParent(idx2);
if(pr1<pr2)
parent[pr2] = pr1;
else
parent[pr1] = pr2;
}
int main(){
cin >> N >> M;
for (int i = 0; i < N;i++){
for (int j = 0; j < M;j++)
cin >> arr[i][j];
}
for (int i = 0; i < N;i++){
for (int j = 0; j < M;j++){
if(arr[i][j] && visited[i][j]==0){
dfs(i, j);
idx++;
}
}
}
memset(distArr, INF, sizeof(distArr));
for (int i = 1; i < idx;i++)
calcDist(i);
for (int i = 1; i < idx;i++){
for (int j = i+1; j < idx;j++){
if(distArr[i][j]==INF)
continue;
edges.push_back(mp(distArr[i][j], mp(i, j)));
}
}
sort(edges.begin(), edges.end());
int ans = 0;
int cntEdge = 0;
/* MST*/
for (int i = 0; i < edges.size();i++){
int pr1 = findParent(edges[i].second.first);
int pr2 = findParent(edges[i].second.second);
if(pr1==pr2)
continue;
cntEdge++;
ans += edges[i].first;
unionParent(edges[i].second.first, edges[i].second.second);
}
if(cntEdge!=idx-2)
ans=-1;
cout << ans;
}
'Algorithm > BOJ' 카테고리의 다른 글
[백준/BOJ] 25963 계단 만들기 (Small) - 접근과 풀이 (c++) (0) | 2022.12.01 |
---|---|
[백준/BOJ] 2887 행성 터널 - 접근과 풀이 (c++) (0) | 2022.11.23 |
[백준/BOJ] 1774 우주신과의 교감 - 접근과 풀이 (c++) (0) | 2022.11.22 |
[백준/BOJ] 1285 동전 뒤집기 - 접근과 풀이 (c++) (0) | 2022.11.21 |
[백준/BOJ] 2629 양팔저울 - 접근과 풀이 (c++) (0) | 2022.11.20 |