2023. 2. 12. 22:55ㆍ🧡 Programming/💻 코딩 테스트
파란것은 섬이고 하얀것은 바다
그리고 회색의 다리로 섬들을 최소 길이로 이으라는 문제이다.
문제 자체는 이해하기 어렵지 않다.
다리는 직선이어야 하고 길이는 2이상이어야 한다.
그리고 다리는 섬과 섬 사이를 이어야한다. 즉, 다리를 잇는 도중에 다리를 짓는 방향과 다른방향으로 섬이 있다고 해서 섬이 이어졌다고 판단하면 안된다.
그런데 이 문제를 푸는 방법은 좀 생각을 잘 해야 한다.
코드를 보면 좀 복잡 해 보이겠지만 함수의 이름을 최대한 직관적으로 쉽게 지정해 놓았으니 찬찬히 보면서 이해해 보면 좋을 것 같다.
일단 풀이의 알고리즘 순서는 다음과 같다.
1.dfs()를 이용하여 일단 섬의 개수를 구한다.
- dfs를 이용하여 동서남북 4방향 다 체크하면서 더 이상 갈 곳이 없으면 빠져나와서 섬 개수를 카운트 한다.
2. findBridge()를 이용하여 각 섬들간의 최소 다리 길이를 구한다.
- 1번 섬과 2번 섬을 잇는 다리의 최소 길이는 a
- 2번 섬과 3번 섬을 잇는 다리의 최소 길이는 b
- n번 섬과 n+1번 섬을 잇는 다리의 최소 길이는 x.. 이런식으로 구한다.
3. 구한 최소 길이의 다리들을 모두 select_bridge라는 벡터에 넣어두고 조합을 계산한다. ( comb(C,inx,cnt) )
- 예) select_bridge의 길이가 m 이라고 하였을 때, 개의 다리중에서 1,2,5,m-1 번 다리만 사용 하였을 경우, 모든 섬들을 다 잇는지 확인한다. ( CheckConnection() )
4. CheckConnection()에서 모든 섬들을 다 잇는 경우에 다리의 길이가 최소 길이인지 확인한다. (bridge_total_len)
#include <stdio.h>
#include <queue>
#include <vector>
#include <string.h>
int Y, X;
int map[15][15];
int mapnum[15][15];
bool visit[15][15];
int dx[] = { 0,0,1,-1 };
int dy[] = { 1,-1,0,0 };
int island_num = 0;
struct info { int sn, en, len; };
std::vector<info> bridge;
std::vector<bool> select_bridge;
int ans = 987654321;
int bridge_total_len = 0;
bool connection[10][10];
bool connection_visit[10];
void dfs(int y, int x, int n) { //cnt islands
visit[y][x] = true;
mapnum[y][x] = n;
for (int i = 0; i < 4; i++) {
int ny = y + dy[i];
int nx = x + dx[i];
if (ny < 0 || nx < 0 || ny >= Y || nx >= X) continue;
if (map[ny][nx] == 0 || visit[ny][nx]) continue;
dfs(ny, nx, n);
}
}
int findbridge(int sn, int en, int y, int x, int minlen_bridge) {
int len = 0;
for (int i = 0; i < 4; i++) {
int ny = y + dy[i];
int nx = x + dx[i];
len = 0;
while (true) {
if (ny < 0 || nx < 0 || ny >= Y || nx >= X) break;
if (mapnum[ny][nx] != en && map[ny][nx] == 1) break;///이게 중요!
if (map[ny][nx] == 0) {
len++;
ny += dy[i];
nx += dx[i];
continue;
}
else if (mapnum[ny][nx] == en && len >= 2) {
//printf("len : %d \n", len);
minlen_bridge = len < minlen_bridge ? len : minlen_bridge;
break;
}
else//
break;
}
}
return minlen_bridge;
}
void checkConnection() {
memset(connection, 0, sizeof(connection));
memset(connection_visit, 0, sizeof(connection_visit));
for (int i = 0; i < bridge.size(); i++) {
if (select_bridge[i]) {
connection[bridge[i].sn][bridge[i].en] = true;
connection[bridge[i].en][bridge[i].sn] = true;
}
}
std::queue<int> Q;
Q.push(1);
connection_visit[1] = true;
int island_cnt = 1;
bool Flag = false;
while (!Q.empty()) {
int cur = Q.front();
Q.pop();
if (island_cnt == island_num) {
Flag = true;
break;
}
for (int i = 1; i <= island_num; i++) {
if (cur == i) continue;
if (connection[cur][i] && !connection_visit[i]) {
connection_visit[i] = true;
Q.push(i);
island_cnt++;
}
}
}
if (Flag) {
ans = ans > bridge_total_len ? bridge_total_len : ans;
}
}
void comb(int C, int inx, int cnt) { // combination 하는법
if (cnt == C) {
bridge_total_len = 0;
for (int i = 0; i < bridge.size(); i++) {
if (select_bridge[i]) bridge_total_len += bridge[i].len;
}
checkConnection();
return;
}
for (int i = inx; i < bridge.size(); i++) {
if (select_bridge[i]) continue;
select_bridge[i] = true;
comb(C, i + 1, cnt + 1);
select_bridge[i] = false;
}
}
int main() {
scanf("%d %d", &Y, &X);
for (int y = 0; y < Y; y++) for (int x = 0; x < X; x++) scanf("%d", &map[y][x]);
// calculate island num
for (int y = 0; y < Y; y++) {
for (int x = 0; x < X; x++) {
if (map[y][x] == 0 || visit[y][x]) continue;
island_num++;
dfs(y, x, island_num);
}
}
// find bridge
for (int sn = 1; sn <= island_num; sn++) {
for (int en = sn + 1; en <= island_num; en++) {
if (sn >= en) continue;
int minlen_bridge = 999999;
int minlen_bridge_cand = 999999;
for (int y = 0; y < Y; y++) {
for (int x = 0; x < X; x++) {
if (mapnum[y][x] != sn) continue;
minlen_bridge_cand = findbridge(sn, en, y, x, 99999999);
//printf("%d \n", minlen_bridge_cand);
minlen_bridge = minlen_bridge > minlen_bridge_cand ? minlen_bridge_cand : minlen_bridge;
}
}
if (minlen_bridge != 999999) {
bridge.push_back({ sn,en,minlen_bridge });
select_bridge.push_back(false);
}
}
}
for (int C = 1; C <= bridge.size(); C++) {
for (int i = 0; i < select_bridge.size(); i++) select_bridge[i] = false;
comb(C, 0, 0);
}
if (ans == 987654321) printf("-1\n");
else printf("%d\n", ans);
return 0;
}
삼성 코테를 준비하면서 느낀건데 정신을 바짝 차려야 한다.
시뮬레이션이지만 적어도 이런 조합,순열,bfs,dfs를 열심히 돌려서 풀 수 있는 문제라면 희망의 끈을 놓지않고 실수하지 않는 연습을 해야 한다.
나의 경우에는 함수와 변수명의 길이가 길더라도 풀어서 쓰는 것이 그 방법이었다.
왜냐면 코딩을 하다보면 내가 무엇을 하고있는 지 까먹기 때문..
이 함수의 input과 output을 정확히 기억하도록 하자.
그리고 문제를 풀다보면 내가 실수하는 유형이 좀 정해져 있다.
나의 경우에는 일단 오타였고 또 map의 크기, 조건등을 종종 꼼꼼하게 읽지 않아서 틀리는 경우였다.
'🧡 Programming > 💻 코딩 테스트' 카테고리의 다른 글
[백준/C++] 17142 연구소3 (BFS) (0) | 2023.02.18 |
---|---|
[백준/C++] 14500 테트로미노 (DFS) (0) | 2023.02.15 |
[SWEA/C++] 4615 재미있는 오셀로 게임 (DFS, 쉽고 짧은 코드) (0) | 2023.02.12 |
[백준/C++] 7576번 토마토 (BFS기초문제, 방문체크 순서, vector 사용하면 안되는 이유) (0) | 2023.02.11 |
[백준/C++] 17143번 낚시왕 (반례 케이스) (0) | 2023.02.11 |