[백준] 2146번 다리 만들기 (= bridge building) - 재우스 프로그래밍 (C 언어)
2021. 1. 29. 11:16ㆍ백준
문제 링크 : www.acmicpc.net/problem/2146
#include <stdio.h>
#include <stdbool.h>
#define DEBUG 0
#define MAX_MAP_SIZE 100
typedef struct _Node {
int islandNum; //0은 바다, 1은 번호 안 준 육지, 2부터 번호 부여한 육지
bool side; //true이면 side(다리를 놓을 수 있음), false이면 다리를 놓을 수 없음
} Node;
typedef struct _QueueNode {
int xPos;
int yPos;
int length;
} QueueNode;
typedef struct _Queue {
QueueNode arrQueue[MAX_MAP_SIZE * MAX_MAP_SIZE];
int front;
int back;
} Queue;
Queue queue;
int count; //arrSideLand에 실제 저장된 데이터 개수
int mapSize; //입력될 좌표의 한 변 길이
Node graph[MAX_MAP_SIZE][MAX_MAP_SIZE]; //graph 크기는 mapSize * mapSize
bool visited[MAX_MAP_SIZE][MAX_MAP_SIZE]; //방문을 체크하는 배이므로 graph와 동일한 크기
QueueNode arrSideLand[MAX_MAP_SIZE * MAX_MAP_SIZE]; //최대 섬의 개수는 전체 좌표 개수보다 작다.
//x좌표, y좌표, 섬의 번호를 한 노드에 저장한다.
void giveIslandNumBFS(int, int, int); //섬에 숫자를 부여해주는 함수
void sideLand(void); //arrSideLand에 0이 붙어있는 모든 육지 좌표 저장
int minRouteBFS(int, int, int, int);
void checkSide(void); //graph의 모든 좌표에 대해 옆에 바다(= 0)이 붙어 있는지 없는지에 대해 저장
void push(int, int, int);
bool empty(void);
void initNode(Node *);
void initGraph(void);
void initVisited(void);
void initQueue(void);
#if DEBUG
void printGraph(void); //graph 출력하는 함수
#endif
int main(void)
{
scanf("%d", &mapSize);
initQueue(); //queue 초기화
initGraph(); //graph 초기화
//graph 값 입력 받아서 저장
for (int i = 0; i < mapSize; i++) {
for (int j = 0; j < mapSize; j++) {
scanf("%d", &graph[i][j].islandNum);
}
}
int islandNum = 2; //각 섬에 부여할 숫자
//1로 저장한 육지를 섬 단위로 숫자 2부터 부여
for (int i = 0; i < mapSize; i++) {
for (int j = 0;j < mapSize; j++) {
if (graph[i][j].islandNum == 1) {
giveIslandNumBFS(i, j, islandNum); //(i, j) 위치에 연결된 육지에 islandNum 값으로 섬에 숫자 부여
islandNum++; //다음 섬의 숫자
}
}
}
//graph 출력
#if DEBUG
printGraph();
#endif
int min = mapSize + mapSize - 2; //다리 길이의 최솟값 저장
int temp = min;
checkSide(); //섬의 가장자리가 될 수 있는 조건 중 하나가 옆에 0인 바다가 최소 한개 이상 붙어있어야 하므로 옆에 바다가 붙어있는지 여부를 true false로 저장
sideLand(); //섬의 가장자리 육지만 필요하므로 뽑아서 따로 배열 생성
for (int i = 0; i < count; i++) {
for (int j = 0; j < count; j++) {
//현재 좌표의 섬 번호와 다른 섬 번호라면
if (arrSideLand[j].length != arrSideLand[i].length) {
initQueue(); //queue 초기화
initVisited(); //방문 여부 초기화
visited[arrSideLand[j].xPos][arrSideLand[j].yPos] = false; //목표 섬은 방문되어 있다는 표시를 지워줘야 도달 가능
temp = minRouteBFS(arrSideLand[i].xPos, arrSideLand[i].yPos, arrSideLand[j].xPos, arrSideLand[j].yPos); //i 섬 좌표로부터 j 섬 좌표까지의 거리 계산
//한 개의 섬에 둘러싸야 있는 경로는 목표에 도달하지 못하는 경우가 생김(옆이 바다가 있다고 해서 무조건 다른 섬에 도달 가능한 경우가 아니기 때문)
if (temp == -1) {
continue;
}
//섬 사이의 거리가 최소 2이므로 더 이상 탐색할 필요가 없을 때는 빠르게 프로그램 종료를 시켜준다.
else if (temp == 2) {
printf("1\n");
return 0;
}
min = min > temp ? temp : min; //최솟값 갱신
}
}
}
printf("%d\n", min - 1);
return 0;
}
//모든 섬에 2, 3, 4, ... 순서로 번호 부여하는 함수
void giveIslandNumBFS(int xPos, int yPos, int islandNum)
{
initQueue(); //queue 초기화
push(xPos, yPos, 0); //숫자 부여할 섬의 첫 육지를 queue에 push
graph[xPos][yPos].islandNum = islandNum; //queue에 push한 좌표는 islandNum 숫자 부여
queue.front++; //queue의 front 증가
while (!empty()) {
int frontXPos = queue.arrQueue[queue.front].xPos;
int frontYPos = queue.arrQueue[queue.front].yPos;
//아래쪽 좌표가 islandNum 1인 초기 육지일 때
if (frontXPos - 1 >= 0 && graph[frontXPos - 1][frontYPos].islandNum == 1) {
push(frontXPos - 1, frontYPos, 0); //아래쪽 좌표를 queue에 삽입
graph[frontXPos - 1][frontYPos].islandNum = islandNum; //queue에 삽입하는 좌표의 섬 번호 부여(앞으로 탐색이 안됨)
}
//위쪽 좌표가 islandNum 1인 초기 육지일 때
if (frontXPos + 1 < mapSize && graph[frontXPos + 1][frontYPos].islandNum == 1) {
push(frontXPos + 1, frontYPos, 0); //상동
graph[frontXPos + 1][frontYPos].islandNum = islandNum;
}
//왼쪽 좌표가 islandNum 1인 초기 육지일 때
if (frontYPos - 1 >= 0 && graph[frontXPos][frontYPos - 1].islandNum == 1) {
push(frontXPos, frontYPos - 1, 0); //상동
graph[frontXPos][frontYPos - 1].islandNum = islandNum;
}
//오른쪽 좌표가 islandNum 1인 초기 육지일 때
if (frontYPos + 1 < mapSize && graph[frontXPos][frontYPos + 1].islandNum == 1) {
push(frontXPos, frontYPos + 1, 0); //상동
graph[frontXPos][frontYPos + 1].islandNum = islandNum;
}
queue.front++; //queue에서 탐색할 다음 데이터 가리킴
}
return;
}
//바다와 연결된 모든 섬 좌표와 섬 번호를 arrSideLand 에 저장
void sideLand(void)
{
for (int i = 0; i < mapSize * mapSize; i++) {
arrSideLand[i].xPos = -1;
arrSideLand[i].yPos = -1;
arrSideLand[i].length = 0;
}
for (int i = 0; i < mapSize; i++) {
for (int j = 0; j < mapSize; j++) {
//섬 번호가 2 이상으로 부여 받고 && 옆에 바다가 있는 좌표라면 배열에 저장
if (graph[i][j].islandNum > 1 && graph[i][j].side) {
arrSideLand[count].xPos = i;
arrSideLand[count].yPos = j;
arrSideLand[count].length = graph[i][j].islandNum;
count++;
}
}
}
return;
}
//섬 번호가 다른 두 좌표 사이의 최단 거리를 계산해서 return 하는 함수
//경로가 없으면 return -1
int minRouteBFS(int startX, int startY, int endX, int endY)
{
push(startX, startY, 0); //출발 좌표 queue에 삽입
queue.front++; //-1에 있던 front 0으로 증가
//도착 좌표에 도달한다면 두 좌표 사이의 거리를 return 하지만
//도착 좌표에 결국 도달하지 못하고 queue가 비어버린다면 경로가 섬 내부에 갇히는 것이므로 -1 return
//1 1 1 0
//1 0 1 0
//1 1 1 0
//0 0 0 1 에서 (0, 1) 좌표는 바다를 끼고 있지만 (3, 3)에 도달 불가능
while (!empty()) {
int frontXPos = queue.arrQueue[queue.front].xPos;
int frontYPos = queue.arrQueue[queue.front].yPos;
int frontLength = queue.arrQueue[queue.front].length;
//현재 좌표가 위쪽 벽에 붙어있지 않고 && 위쪽 좌표가 경로계산을 위한 방문을 아직 하지 않았을 때
if (frontXPos - 1 >= 0 && !visited[frontXPos - 1][frontYPos]) {
push(frontXPos - 1, frontYPos, frontLength + 1); //위쪽 좌표 값과 거리 1 증가시켜서 queue에 삽입
visited[frontXPos - 1][frontYPos] = true; //queue에 넣은 좌표는 방문했다고 check
//만약 위쪽 좌표가 최종 좌표라면 저장된 거리 반환
if (visited[endX][endY]) {
return queue.arrQueue[queue.back].length;
}
}
//아래쪽 좌표로 상동
if (frontXPos + 1 < mapSize && !visited[frontXPos + 1][frontYPos]) {
push(frontXPos + 1, frontYPos, frontLength + 1);
visited[frontXPos + 1][frontYPos] = true;
if (visited[endX][endY]) {
return queue.arrQueue[queue.back].length;
}
}
//왼쪽 좌표로 상동
if (frontYPos - 1 >= 0 && !visited[frontXPos][frontYPos - 1]) {
push(frontXPos, frontYPos - 1, frontLength + 1);
visited[frontXPos][frontYPos - 1] = true;
if (visited[endX][endY]) {
return queue.arrQueue[queue.back].length;
}
}
//오른쪽 좌표로 상동
if (frontYPos + 1 < mapSize && !visited[frontXPos][frontYPos + 1]) {
push(frontXPos, frontYPos + 1, frontLength + 1);
visited[frontXPos][frontYPos + 1] = true;
if (visited[endX][endY]) {
return queue.arrQueue[queue.back].length;
}
}
queue.front++;
}
return -1;
}
//바다와 붙어 있는 육지에는 side 변수에 true 저장
void checkSide(void)
{
for (int i = 0; i < mapSize; i++) {
for (int j = 0; j < mapSize; j++) {
if (graph[i][j].islandNum > 0) {
//위쪽 좌표가 바다(= 0)라면 true 저장
//네 방면중 한 곳이 있든 전부 있든 상관 없으므로 하나라도 통과되면 더 이상 검사 안함
if (i - 1 >= 0 && graph[i - 1][j].islandNum == 0) {
graph[i][j].side = true;
}
else if (i + 1 < mapSize && graph[i + 1][j].islandNum == 0) {
graph[i][j].side = true;
}
else if (j - 1 >= 0 && graph[i][j - 1].islandNum == 0) {
graph[i][j].side = true;
}
else if (j + 1 < mapSize && graph[i][j + 1].islandNum == 0) {
graph[i][j].side = true;
}
}
}
}
return;
}
void push(int xPos, int yPos, int length)
{
queue.back++; //queue의 back 증가
queue.arrQueue[queue.back].xPos = xPos;
queue.arrQueue[queue.back].yPos = yPos;
queue.arrQueue[queue.back].length = length;
return;
}
bool empty()
{
if (queue.front > queue.back) {
return true;
}
else {
return false;
}
}
void initNode(Node *inputNode)
{
inputNode->islandNum = 0;
inputNode->side = false;
return;
}
void initGraph()
{
for (int i = 0; i < MAX_MAP_SIZE; i++) {
for (int j = 0; j < MAX_MAP_SIZE; j++) {
initNode(&graph[i][j]);
}
}
return;
}
void initVisited(void)
{
for (int i = 0; i < MAX_MAP_SIZE; i++) {
for (int j = 0; j < MAX_MAP_SIZE; j++) {
if (graph[i][j].islandNum > 0) {
visited[i][j] = true;
}
else {
visited[i][j] = false;
}
}
}
return;
}
void initQueue()
{
for (int i = 0; i < MAX_MAP_SIZE * MAX_MAP_SIZE; i++) {
queue.arrQueue[i].xPos = -1;
queue.arrQueue[i].yPos = -1;
queue.arrQueue[i].length = -1;
}
queue.front = -1;
queue.back = -1;
return;
}
#if DEBUG
void printGraph(void)
{
printf("graph\n");
for (int i = 0; i < mapSize; i++) {
for (int j = 0; j < mapSize; j++) {
printf("%d ", graph[i][j].islandNum);
}
printf("\n");
}
printf("\n");
return;
}
#endif
'백준' 카테고리의 다른 글
[백준] 12865번 평범한 배낭 (= plain backpack) - 재우스 프로그래밍 (C 언어) (0) | 2021.01.30 |
---|---|
[백준] 15954번 인형들 (= dolls) - 재우스 프로그래밍 (C 언어) (0) | 2021.01.30 |
[백준] 1967번 트리의 지름 (= tree diameter) - 재우스 프로그래밍 (C 언어) (0) | 2021.01.29 |
[백준] 1062번 가르침 (= teaching) - 재우스 프로그래밍 (C 언어) (0) | 2021.01.29 |
[백준] 14226번 이모티콘 (= emoticon) - 재우스 프로그래밍 (C 언어) (0) | 2021.01.29 |