알고리즘

[programmers] 고고학 최고의 발견

졔졔311 2023. 5. 19. 17:09
728x90
반응형

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

 

프로그래머스

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

programmers.co.kr

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

 

경우의 수 - 중복순열

 

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

 

clock의 첫 번째 row부터 정해나간다.

4번 회전시키면 원래 방향으로 돌아오므로, 0~3번 회전시키는 것만이 의미가 있다.

따라서, 첫 번째 row의 모든 clock을 0~3회 회전시킨다.

 

첫 번째 row의 회전 결과가 정해지면, 아래 row로 넘어가 위 row를 모두 0으로 만드는 과정을 거친다.

이미 회전시킨 위의 row를 바꿀 방법은 바로 아래의 row에서 clock을 회전시키는 방법 뿐이므로,

위를 0으로 만들도록 회전시킨다. 즉, 첫 번째 이후의 row들을 회전시키는 방법은 이미 정해져 있다.

이 방식으로 아래 row들을 계속해서 완성해 나갔을 때, 모두 0으로 만들어진다면 그 때의 cost의 min값을 찾으면 된다.

#include <string>
#include <vector>
#include <limits.h>

using namespace std;

// 위, 오, 아, 왼
int dx[4] = {-1, 0, 1, 0};
int dy[4] = {0, 1, 0, -1};
int clock_rots[10][10] = {0, };
vector<vector<int>> clocks;
int answer, N;

bool is_complete(){
    for(int i = 0; i < N; i++){
        for(int j = 0; j < N; j++){
            if(clocks[i][j] != 0){
                return false;
            }
        }
    }
    return true;
}

// x,y 기준 인접한 모든 곳 90도 시계방향 회전
void rotate(int x, int y){
    clocks[x][y] = (clocks[x][y]+1)%4;
    for(int i = 0; i < 4; i++){
        int nx = x + dx[i];
        int ny = y + dy[i];
        if(nx < 0 || nx >= N || ny < 0 || ny >= N){
            continue;
        }
        clocks[nx][ny] = (clocks[nx][ny]+1)%4;
    }
}

// x,y 기준 인접한 모든 곳 90도 반시계방향 회전
void restore(int x, int y){
    clocks[x][y]--;
    if(clocks[x][y] < 0) clocks[x][y] = 3;
    for(int i = 0; i < 4; i++){
        int nx = x + dx[i];
        int ny = y + dy[i];
        if(nx < 0 || nx >= N || ny < 0 || ny >= N){
            continue;
        }
        clocks[nx][ny]--;
        if(clocks[nx][ny] < 0) clocks[nx][ny] = 3;
    }
}

// 윗줄에 맞춰서 아랫줄 돌리기
void decide_all(int cost, int row){
    if(is_complete()){
        if(cost < answer){
            answer = cost;
        }
        return;
    }
    if(row >= N || cost >= answer){
        return;
    }
    vector<int> rotates;
    for(int i = 0; i < N; i++){
        int cur_rots = 0;
        while(clocks[row-1][i] != 0){
            rotate(row, i);
            cost++;
            cur_rots++;
        }
        rotates.push_back(cur_rots);
    }
    decide_all(cost, row+1);
    for(int i = 0; i < N; i++){
        while(rotates[i]--){
            restore(row, i);
        }
    }
}

void decide_first_row(int col, int cost){
    if(col >= N){
        decide_all(cost, 1);
        return;
    }
    int cur_cost = 0;
    // 돌리지 않고 다음으로 넘어감
    decide_first_row(col+1, cost+cur_cost);
    // 1~3번 돌리고 다음으로 넘어감
    for(int i = 0; i < 3; i++){
        rotate(0, col);
        cur_cost++;
        decide_first_row(col+1, cost + cur_cost);
    }
    while(cur_cost--){
        restore(0, col);
    }
}

// 시계가 행렬을 이룸. 하나의 시계에 시곗바늘 하나씩.
// 한 번에 90도씩 시계방향으로 돌릴 수 있음.
// 상하좌우 인접한 시계도 함께 돌아감.
// 모든 시계가 12시를 가리키면 종료. 최소의 조작 횟수=?
int solution(vector<vector<int>> clockHands) {
    answer = INT_MAX;
    clocks = clockHands; N = clockHands.size();
    // 맨 윗줄을 정해줌. => 각각 0~3번씩 돌림 => max : 4^8
    decide_first_row(0, 0);
    return answer;
}

 

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

dfs로 모든 케이스를 찾으려 했을 때, 계속해서 시간초과로 고생했다.

아무리 가지치기를 해도 불가능했다.

row, col을 넘겨주며 그 이후부터 돌리는 방식을 했지만, 최대 길이가 8일다보니, 경우의 수가 너무 증가했다.

 

첫 번째 row를 정해주면, 다음 row의 clock들은 위의 row를 모두 0으로 만드는 방향으로만 회전할 수 있다는 것을 깨닫고, 이 방식으로 구현해 성공했다.

첫 row를 정한는 것은 중복순열이므로 최대 4^8이다.

728x90
반응형