알고리즘

[programmers] 빛의 경로 사이클

졔졔311 2023. 8. 26. 22:53
728x90
반응형

https://school.programmers.co.kr/learn/courses/30/lessons/86052

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

---------------------------------------------------핵심 알고리즘--------------------------------------------

 

재귀

 

---------------------------------------------------풀이----------------------------------------------------

 

이 문제를 보자마자 생각난 것은 특정 격자 칸에서 어떤 방향으로 이동할지를 고려해야 한다는 점이다.

그 방향으로 이동했던 적이 있다면, 사이클이 생긴 것이다.

따라서, 이를 저장하기 위한 자료구조로 bool visited[x좌표][y좌표][이동할방향]를 사용하였다.

 

main이 되는 solution()함수는 다음과 같다.

모든 격자 칸에 대해, 네 방향(상하좌우)에서 빛이 들어왔을 경우를 생각하면 모든 케이스를 고려할 수 있다.

따라서, 모든 (x,y)에 대해, 그리고 각각에서 네 방향 dir에 대해,

go_light(x,y,dir)을 호출해 빛을 쏘고 리턴 값으로 그 때의 빛의 길이를 받았다.

만약 이 값이 0이면 사이클이 생기지 않았거나 이미 계산한 사이클이므로 저장하지 않는다.

마지막으로, answer에 저장된 사이클 길이들을 오름차순으로 정리해 리턴한다.

 

go_light(int x, int y, int dir) 함수는

(x,y)로 dir 방향에서 빛이 들어왔을 때 빛의 경로 길이를 리턴하는 함수이다.

dir은 상,우,하,좌(시계방향)의 순서대로 0,1,2,3을 의미한다.

우선, 들어온 빛이 (x,y) 격자를 지난 경우 바뀌는 방향을 계산해 dir을 갱신한다.

그 방향으로 이미 빛이 나간 적이 있으면(visited[x][y][dir] == true), 사이클이 생긴 것이므로 더 이상 방문하지 않고 0을 리턴한다.

아니라면, 방문한 것으로 처리(visited[x][y][dir] = true)하고 다음 위치로 이동한 경우를 계산한다.

즉, 현재 이동할 경로 길이(1) + 다음 위치로 이동한 경우의 계산 값(go_light(next_x, next_y, dir))을 리턴한다.

 

#include <string>
#include <vector>
#include <cstring>
#include <algorithm>
#include <iostream>

using namespace std;

vector<string> grid;
// 상, 우, 하, 좌
int dx[4] = {-1, 0, 1, 0};
int dy[4] = {0, 1, 0, -1};
// visited[x좌표][y좌표][이동방향]
bool visited[501][501][4] = {false, };

void init_visited(){
    memset(visited, sizeof(visited), false);
}

// 격자의 글자에 따라 방향 수정해 리턴
int change_dir(int x, int y, int dir){
    switch(grid[x][y]){
        case 'S':
            break;
        case 'L':
            dir--;
            if(dir < 0){
                dir = 3;
            }
            break;
        case 'R':
            dir = (++dir)%4;
            break;
    }
    return dir;
}

int calc_next_x(int x, int dir){
    x += dx[dir];
    if(x < 0){
        x = grid.size()-1;
    }
    return x % grid.size();
}

int calc_next_y(int y, int dir){
    y += dy[dir];
    if(y < 0){
        y = grid[0].length()-1;
    }
    return y % grid[0].length();
}

// 이동한 위치 (x,y)와 빛이 들어온 방향 dir.
int go_light(int x, int y, int dir){
    // 빛이 격자에 도달할 경우 이동해야 할(꺾일) 방향으로 dir 수정
    dir = change_dir(x, y, dir);
    // 이미 방문한 방향이면, 그 방향으로 이동하지 않고 종료.
    if(visited[x][y][dir]){
        return 0;
    }
    visited[x][y][dir] = true;
    // 다음 격자 위치로 이동했을 때의 길이를 더해 리턴.
    return 1 + go_light(calc_next_x(x, dir), calc_next_y(y, dir), dir);
}

vector<int> solution(vector<string> _grid) {
    vector<int> answer;
    grid = _grid;
    // 모든 좌표에 대해
    for(int i = 0; i < grid.size(); i++){
        for(int j = 0; j < grid[0].length(); j++){
            // 각 네 방향으로 빛 쏘기
            for(int k = 0; k < 4; k++){
                // (i,j)부터 빛을 쏘고 사이클의 길이를 받아옴
                int tmp = go_light(i, j, k);
                // 길이가 0이면 사이클 생성x => 저장 안함.
                if(tmp == 0){
                    continue;
                }
                answer.push_back(tmp);
            }
        }
    }
    // answer를 오름차순으로 정렬
    sort(answer.begin(), answer.end());
    return answer;
}

 

---------------------------------------------------후기----------------------------------------------------

 

아이디어는 제대로 구상했고 문제가 없었지만, 예외를 생각지 못해 한 번 틀렸던 문제.

어떤 칸에서 시작해서 다른 칸을 모두 도는 것만이 사이클이 아니다.

그 부분을 생각지 못해 (0,0)에서 시작하는 경우만 생각해서 틀렸었다.

즉, 질문하기에 나와있는 예외 케이스를 보고 무엇이 문제인지 찾을 수 있었다.

S
S

위와 같은 격자가 주어질 때, 사이클은 총 6가지가 가능하다.

(0,0)에서 시작하면 좌, 우 방향으로 이동할 경우 (1,0)은 방문하지 않는 사이클이 생성되고,

(1,0)에서 시작하면 좌, 우 방향으로 이동할 경우 (0,0)은 방문하지 않는 사이클이 생성된다.

이 부분을 해결하자 정답 처리가 될 수 있었다.

728x90
반응형

'알고리즘' 카테고리의 다른 글

[programmers] 양과 늑대  (0) 2024.03.04
[programmers] 모음사전  (0) 2023.08.27
[programmers] 전력망을 둘로 나누기  (0) 2023.06.22
[programmers] 교점에 별 만들기  (1) 2023.06.17
[programmers] n^2 배열 자르기  (1) 2023.06.16