ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [백준 19236] 청소년 상어
    C 프로그래밍/BOJ 2022. 10. 24. 17:51
    728x90

    [코드트리] 술래잡기 체스와 동일한 문제

    https://www.codetree.ai/frequent-problems/odd-chess2/description

     

    코드트리

    국가대표가 만든 코딩 공부의 가이드북 코딩 왕초보부터 꿈의 직장 코테 합격까지, 국가대표가 엄선한 커리큘럼으로 준비해보세요.

    www.codetree.ai

    메... 여전히 어려워 상어쉑....

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

     

    19236번: 청소년 상어

    첫째 줄부터 4개의 줄에 각 칸의 들어있는 물고기의 정보가 1번 행부터 순서대로 주어진다. 물고기의 정보는 두 정수 ai, bi로 이루어져 있고, ai는 물고기의 번호, bi는 방향을 의미한다. 방향 bi는

    www.acmicpc.net

    #include <cstdio>
    #include <cstring>
    
    int board[4 + 2][4 + 2];	// 도둑말 번호만 표시해줌 
    
    struct _pt
    {
    	int x, y;
    	int d;
    	bool alive = true;
    };
    _pt pin[16 + 2];
    
    int sx, sy, sd;	// (술래말 위치, 방향)
    int ans = 0;
    
    
    void input()
    {
    	for (int i = 0; i < 4; i++) {
    		for (int j = 0; j < 4; j++) {
    			int p = 0, d = 0;
    			scanf("%d %d", &p, &d);
    			board[i][j] = p;
    			pin[p] = { i, j, d, true };
    		}
    	}
    
    	// 초기에는 (0, 0)에 있는 도둑말을 잡으며 시작한다.
    	sx = 0; sy = 0; sd = pin[board[0][0]].d;
    	ans = board[0][0];
    
    	pin[board[0][0]] = { -1, -1, -1, false };
    	board[0][0] = 0;
    }
    
    
    void move_runner(int sx, int sy)
    {
    	// dir
    	// 1↑, 2↖, 3←, 4↙, 5↓, 6↘, 7→, 8↗
    	int dir_x[9] = { 0, -1, -1, 0, 1,  1, 1, 0, -1 };
    	int dir_y[9] = { 0,  0, -1, -1, -1, 0, 1, 1, 1 };
    
    
    	for (int i = 1; i <= 16; i++) {
    		if (pin[i].alive == false) continue;
    
    		int x = pin[i].x;
    		int y = pin[i].y;
    		int d = pin[i].d;
    
    		for (int p = 0; p < 8; p++) {
    			int nd = (d + p);
    			if (nd > 8) nd -= 8;
    			int nx = x + dir_x[nd];
    			int ny = y + dir_y[nd];
    
    			if (nx < 0 || nx > 4 - 1 || ny < 0 || ny > 4 - 1) continue;
    			if (nx == sx && ny == sy) continue;
    			
    			// 움직이려는 칸에 다른 도둑말이 있다면
    			if (board[nx][ny] != 0) {
    				int tmp = board[nx][ny];
    				board[nx][ny] = i;
    				pin[i] = { nx, ny, nd, true };
    				board[x][y] = tmp;
    				pin[tmp] = { x, y, pin[tmp].d, true };
    				break;
    			}
    			else if (board[nx][ny] == 0){
    				board[x][y] = 0;	// 원래 자기칸은 0으로 만들어줘야 분신술 안씀
    				board[nx][ny] = i;
    				pin[i] = { nx, ny, nd, true };
    				break;
    			}
    		}
    	}
    }
    
    
    
    void DFS(int x, int y, int p, int score)
    {
    	// dir
    	// 1↑, 2↖, 3←, 4↙, 5↓, 6↘, 7→, 8↗
    	static int dir_x[9] = { 0, -1, -1, 0, 1, 1, 1, 0, -1 };
    	static int dir_y[9] = { 0,  0, -1, -1, -1, 0, 1, 1, 1 };
    
    	// 도둑말 이동 
    	move_runner(x, y);
    
    	// 현재 상태 백업 
    	int tmp_board[4 + 2][4 + 2];
    	_pt tmp_pin[16 + 2];
    	memset(tmp_board, 0, sizeof(board));
    	memset(tmp_pin, 0, sizeof(pin));
    	memcpy(tmp_board, board, sizeof(board));
    	memcpy(tmp_pin, pin, sizeof(pin));
    
    
    	// 술래말 이동 
    	int end_game = 0;
    	for (int s = 1; s <= 3; s++) {
    		int nx = x + dir_x[p] * s;
    		int ny = y + dir_y[p] * s;
    		
    		// 영역 벗어나거나 도둑말 없는 경우 
    		if (nx < 0 || nx > 4 - 1 || ny < 0 || ny > 4 - 1 || board[nx][ny] == 0) {
    			end_game++;
    			continue;
    		}
    		// 도둑말 있어서 잡는 경우 
    		if (board[nx][ny] != 0) {
    			int pnum = board[nx][ny];
    			int nd = pin[pnum].d;
    			pin[pnum] = { -1, -1, -1, false };
    			board[nx][ny] = 0;
    
    			DFS(nx, ny, nd, score + pnum);
    			
    			// 조작하기 전의 상태로 원복
    			memcpy(board, tmp_board, sizeof(board));
    			memcpy(pin, tmp_pin, sizeof(pin));
    		}
    	}
    	if (end_game == 3) {
    		if (ans < score) ans = score;
    		return;
    	}
    }
    
    
    int main()
    {
    	input();
    	DFS(sx, sy, sd, ans);	// (현재 술래 말 위치, 방향, 점수)
    	printf("%d\n", ans);
    	return 0;
    }

    ++22.11.12  갱신

    드디어 내 힘으로 청소년 상어를 풀었다. 자꾸 다른 테케 안나와서 진짜 기존 코드 보고싶었지만 꾹 참고 디버깅에 성공함... 

     

    1. 술래말이 1칸 / 2칸 / 3칸 이동할 수 있기 때문에 모든 경우의 수를 탐색하기 위해 DFS를 사용해야 하는 문제이다. 

     

    2. board정보 뿐 만 아니라, 현재 도둑말의 위치와 방향, 죽었는지 살았는지 여부를 저장해 놓은 구조체 pin도 같이 백업시켜 놓고, DFS 가 리턴할 때 원복시켜주어야 했다. 맵의 정보가 달라지는 것과 동일하게 도둑말의 정보도 함께 달라지기 때문이다. 

     

    3. 리턴 조건을 어떻게 걸지 고민하다가, 술래말이 최대 3칸 까지 움직일 수 있는데 그 세번이 모두 영역 밖이거나 해당 칸에 도둑말이 없는 경우라면 ans를 갱신하고 리턴하도록 했다. 

    이렇게 안하고 영역밖일때 ans 갱신 후 리턴해도 된다. 어차피 더 멀리 가봐야 영역밖이기 때문이다. 다만 이 방법으로 구현하는 경우, 해당 칸에 도둑말이 없는 경우는 continue로 스루해주어야 한다. 

     

    4. 도둑말을 이동시킬 때 임시배열이 하나 더 필요할까 했는데, 굳이 사용하지 않아도 된다. 

    그냥 교환시킬 도둑말 번호를 tmp 변수에 받아놓고 구조체 정보 갱신해주면된다. 

    이때 중요한 것은 이동할 칸을 찾으면 break로 for 루프를 빠져나와야 한다는 것이다 ! 이거 안해주면 무한 분신사바가 됨... 

    728x90

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

    [백준 1175] 배달  (0) 2022.10.25
    [백준 1039] 교환  (0) 2022.10.24
    [백준 10217] KCM Travel  (0) 2022.10.24
    [백준 10711] 모래성  (0) 2022.10.23
    [백준 2573] 빙산  (0) 2022.10.23

    댓글

Designed by Tistory.