ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [백준 5022] 연결
    C 프로그래밍/BOJ 2022. 10. 22. 01:01
    728x90

    https://www.acmicpc.net/problem/5022

     

    5022번: 연결

    A1과 A2, 그리고 B1과 B2를 연결하는데 필요한 전선의 길이의 최솟값을 출력한다. 만약, 불가능한 경우에는 "IMPOSSIBLE"을 출력한다.

    www.acmicpc.net

    #include <cstdio>
    #include <queue>
    #include <algorithm>
    int N, M;
    struct _st
    {
    	int sx, sy;
    	int ex, ey;
    }Points[2 + 2];
    
    struct _vt
    {
    	int x, y;
    };
    
    
    struct _qt
    {
    	int x, y;
    	std::vector<_vt> path;
    };
    
    std::queue<_qt> Q;
    int visited[100 + 5][100 + 5];
    int move[100 + 5][100 + 5];
    
    int dir_x[4] = { 0, 0, 1, -1 };
    int dir_y[4] = { 1, -1, 0, 0 };
    
    
    void input()
    {
    	scanf("%d %d", &M, &N);
    	scanf("%d %d %d %d", &Points[0].sy, &Points[0].sx, &Points[0].ey, &Points[0].ex);
    	scanf("%d %d %d %d", &Points[1].sy, &Points[1].sx, &Points[1].ey, &Points[1].ex);
    }
    
    
    int BFS(int type, int x, int y)
    {
    	Q = {};
    	std::fill(&visited[0][0], &visited[0][0] + sizeof(visited) / sizeof(int), 0);
    
    	_qt start = {};
    	start.x = x;
    	start.y = y;
    	start.path.push_back({ x, y });
    	Q.push(start);
    	visited[x][y] = 1;
    	bool set = false;
    
    	_qt data = {};
    	while (!Q.empty()) {
    		data = Q.front();
    		Q.pop();
    
    		if (data.x == Points[type].ex && data.y == Points[type].ey) {
    			set = true;
    			break;
    		}
    
    		for (int p = 0; p < 4; p++) {
            		_qt ndata = {};
    			ndata.x = data.x + dir_x[p];
    			ndata.y = data.y + dir_y[p];
    
    			if (ndata.x < 0 || ndata.x > N || ndata.y < 0 || ndata.y > M) continue;
    			if (ndata.x == Points[1 - type].sx && ndata.y == Points[1 - type].sy) continue;
    			if (ndata.x == Points[1 - type].ex && ndata.y == Points[1 - type].ey) continue;
    			if (visited[ndata.x][ndata.y]) continue;	// 이미 방문한 곳 스루
    			if (move[ndata.x][ndata.y]) continue;		// A나 B가 이미 깔려있는 곳으로는 가지 못함 
    			ndata.path.clear();
    			for (_vt road : data.path) {
    				ndata.path.push_back({ road.x, road.y });
    			}
    			ndata.path.push_back({ ndata.x, ndata.y });
    			Q.push({ ndata });
    			visited[ndata.x][ndata.y] = visited[data.x][data.y] + 1;
    		}
    	}
    	if (set == false) return -1;
    
    	for (_vt route : data.path) {
    		move[route.x][route.y] = type + 1;	// 이동 경로표시
    	}
    	return visited[data.x][data.y] - 1;
    }
    
    
    
    int main()
    {
    	input();
    	// A 먼저 깔아본다 
    	std::fill(&move[0][0], &move[0][0] + sizeof(move) / sizeof(int), 0);
    	int res1_A = BFS(0, Points[0].sx, Points[0].sy);
    	int res1_B = BFS(1, Points[1].sx, Points[1].sy);
    	int total1 = res1_A + res1_B;
    
    	// B 먼저 깔아본다 
    	std::fill(&move[0][0], &move[0][0] + sizeof(move) / sizeof(int), 0);
    	int res2_B = BFS(1, Points[1].sx, Points[1].sy);
    	int res2_A = BFS(0, Points[0].sx, Points[0].sy);
    	int total2 = res2_A + res2_B;
    
    
    	if (res1_B == -1 && res2_A == -1) printf("IMPOSSIBLE\n");
    	else {
    		int min = 0x7fffffff;
    		if (res1_B == -1) min = total2;
    		else if (res2_A == -1) min = total1;
    		else if (res1_B != -1 && res2_A != -1) min = (total1 > total2) ? total2 : total1;
    		printf("%d\n", min);
    	}
    
    	return 0;
    }

    1. "두 직선은 접하면 안된다" 라는 조건 때문에 무진장 애를 먹었다. 

    => BFS에서 상태발전을 위한 Q를 생성할 때 지나온 경로들의 좌표를 저장하기 위한 path 벡터를 선언해 주었다. 위의 코드의 경우 행과 열을 반대로 받았기 때문에 문제에서의 좌표평면이 아니라 행렬로 생각해 본다면, (1, 5)에 도달하였을 때 path에 들어있는 좌표는 (1, 2) (1, 3) (1, 4) 이다. 그리고 마지막에 자기 자신 좌표를 push_back 해준다. 

     

    BFS를 통해 최단거리를 탐색한 후 만약 목적 좌표에 도달했다면, set 플래그를 true로 바꿔주고 실제 경로를 move 배열에 표시해주었다. 

    만약 목적 좌표에 도달하지 못했다면 -1을 리턴해 주었다. 

     

    2. 이 조건과 더불어 필요한 전선의 최솟값을 구해야 하기 때문에 BFS를 4번 돌려야 한다.

    => (1)A 최소로 깔고 + (2)B 깔기,  (3)B 최소로 깔고 + (4)A 깔기

    => BFS를 실행할 때 마다 visited 배열(BFS 탐색 시 사용하는 방문배열) 초기화가 필요하고, move 배열(각 전선의 경로 저장하는 배열)은 (1 + 2) (3 + 4) 번 진행 전에 초기화가 필요하다. 

     


    ++ 22.10.24

    경로 표시 재귀로 구현해봄... (1404kb, 0ms)

    #include <cstdio>
    #include <queue>
    #include <algorithm>
    int N, M;
    struct _st
    {
    	int sx, sy;
    	int ex, ey;
    }Points[2 + 2];
    
    struct _qt
    {
    	int x, y;
    };
    
    
    std::queue<_qt> Q;
    int visited[100 + 5][100 + 5];
    int move[100 + 5][100 + 5];
    _qt path[100 + 5][100 + 5];
    
    int dir_x[4] = { 0, 0, 1, -1 };
    int dir_y[4] = { 1, -1, 0, 0 };
    
    
    void input()
    {
    	scanf("%d %d", &M, &N);
    	scanf("%d %d %d %d", &Points[0].sy, &Points[0].sx, &Points[0].ey, &Points[0].ex);
    	scanf("%d %d %d %d", &Points[1].sy, &Points[1].sx, &Points[1].ey, &Points[1].ex);
    }
    
    void path_init()
    {
    	for (int i = 0; i <= M; i++) {
    		for (int j = 0; j <= N; j++) {
    			path[i][j] = {};
    		}
    	}
    }
    
    
    void path_2_move(int x, int y)
    {
    	// 이동 경로 표시
    	if (x == -1 && y == -1) return;
    
    	move[x][y] = 1;
    	path_2_move(path[x][y].x, path[x][y].y);
    }
    
    
    int BFS(int type, int x, int y)
    {
    	Q = {};
    	std::fill(&visited[0][0], &visited[0][0] + sizeof(visited) / sizeof(int), 0);
    	
    	Q.push({x, y});
    	visited[x][y] = 1;
    	path[x][y].x = -1;	path[x][y].y = -1;
    
    	_qt data = {};
    	while (!Q.empty()) {
    		data = Q.front();
    		Q.pop();
    
    		if (data.x == Points[type].ex && data.y == Points[type].ey) {
    			path_2_move(Points[type].ex, Points[type].ey);
    			return visited[data.x][data.y] - 1;
    		}
    
    		for (int p = 0; p < 4; p++) {
    			int nx = data.x + dir_x[p];
    			int ny = data.y + dir_y[p];
    
    			if (nx < 0 || nx > N || ny < 0 || ny > M) continue;
    			if (nx == Points[1 - type].sx && ny == Points[1 - type].sy) continue;
    			if (nx == Points[1 - type].ex && ny == Points[1 - type].ey) continue;
    			if (visited[nx][ny]) continue;	// 이미 방문한 곳 스루
    			if (move[nx][ny]) continue;		// A나 B가 이미 깔려있는 곳으로는 가지 못함 
    			Q.push({ nx, ny });
    			visited[nx][ny] = visited[data.x][data.y] + 1;
    			path[nx][ny].x = data.x; path[nx][ny].y = data.y;
    		}
    	}
    	return -1;
    }
    
    
    int main()
    {
    	input();
    	// A 먼저 깔아본다 
    	path_init();
    	std::fill(&move[0][0], &move[0][0] + sizeof(move) / sizeof(int), 0);
    	int res1_A = BFS(0, Points[0].sx, Points[0].sy);
    	path_init();
    	int res1_B = BFS(1, Points[1].sx, Points[1].sy);
    	int total1 = res1_A + res1_B;
    
    	// B 먼저 깔아본다 
    	path_init();
    	std::fill(&move[0][0], &move[0][0] + sizeof(move) / sizeof(int), 0);
    	int res2_B = BFS(1, Points[1].sx, Points[1].sy);
    	path_init();
    	int res2_A = BFS(0, Points[0].sx, Points[0].sy);
    	int total2 = res2_A + res2_B;
    
    
    	if (res1_B == -1 && res2_A == -1) printf("IMPOSSIBLE\n");
    	else {
    		int min = 0x7fffffff;
    		if (res1_B == -1) min = total2;
    		else if (res2_A == -1) min = total1;
    		else if (res1_B != -1 && res2_A != -1) min = (total1 > total2) ? total2 : total1;
    		printf("%d\n", min);
    	}
    
    	return 0;
    }
    728x90

    'C 프로그래밍 > BOJ' 카테고리의 다른 글

    [백준 2573] 빙산  (0) 2022.10.23
    [백준 3197] 백조의 호수  (0) 2022.10.22
    [백준 23290] 마법사 상어와 복제  (0) 2022.10.15
    [백준 21610] 마법사 상어와 비바라기  (0) 2022.10.15
    [백준 21609] 상어 중학교  (0) 2022.10.13

    댓글

Designed by Tistory.