궤도

[백준] 17406번 : 배열 돌리기 4 본문

💻 현생/⛓ 알고리즘

[백준] 17406번 : 배열 돌리기 4

영이오 2021. 6. 29. 17:26

문제

 


풀이

 

예전엔 구현 문제가 정말 싫었는데 요즘은 좀 괜찮은 것 같다. 풀고나서 성취감이 커서 그런가 싶다.

 

회전의 순서는 next_permutation으로 정했다.

각 연산에 대해 s개의 정사각형이 움직이게 되는데...이걸 그림으로 표현하면

이렇게 된다.

 

제일 큰 문제가 회전일 텐데...나는 큐를 썼다. 자세한건 코드를 보면서 설명하겠다.


소스코드

 

#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>

using namespace std;

struct info {
    int idx, r, c, s;
};

vector<vector<int>> matrix;
pair<int, int> dir[4] = {{0,  1},  //우
                         {1,  0},  //하
                         {0,  -1}, //좌
                         {-1, 0}}; //상

void rotate(int sr, int sc, int er, int ec) {
    queue<int> q;
    q.push(matrix[sr][sc]); //시작점 값
    int row = sr, col = sc + 1, d = 0; //시작점의 바로 오른쪽 좌표로 초기화
    do {
        q.push(matrix[row][col]); //값 저장
        matrix[row][col] = q.front(); //이전 위치의 값으로 업데이트
        q.pop();
        row += dir[d].first;
        col += dir[d].second;
        if ((row < sr) || (row > er) || (col < sc) || (col > ec)) { //범위를 벗어나면 위치 조정하고 방향 바꾸기
            row = row - dir[d].first + dir[(d + 1) % 4].first;
            col = col - dir[d].second + dir[(d + 1) % 4].second;
            d = (d + 1) % 4;
        }
    } while (row != sr || col != sc + 1); //원위치로 돌아올 때까지
}

int calcArr(int N, int M) { //배열의 값
    int result = 5001, tmp = 0;
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < M; j++) //행의 합
            tmp += matrix[i][j];
        result = min(result, tmp); //최솟값 갱신
        tmp = 0;
    }
    return result;
}

int main() {
    int N, M, K, result = 5001;

    cin >> N >> M >> K;
    matrix.assign(N, vector<int>(M));
    vector<info> rot(K);
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < M; j++)
            cin >> matrix[i][j];
    }
    for (int i = 0; i < K; i++) {
        cin >> rot[i].r >> rot[i].c >> rot[i].s;
        rot[i].idx = i; //인덱스
    }

    vector<vector<int>> tmp = matrix; //임시저장
    do {
        for (int i = 0; i < K; i++) { //회전
            for (int j = 1; j <= rot[i].s; j++) //각 회전 연산에 대해 rot[i].s개의 정사각형을 회전하게 됨
                rotate(rot[i].r - j - 1, rot[i].c - j - 1, //시작점
                       rot[i].r + j - 1, rot[i].c + j - 1); //끝점
        }
        result = min(result, calcArr(N, M)); //최솟값 갱신
        matrix = tmp; //복구
    } while (next_permutation(rot.begin(), rot.end(),
                              [](const info &i1, const info &i2) { //idx에 대해 순열
                                  return i1.idx < i2.idx;
                              }));
    cout << result;
}

원래 90줄 초반인가 그랬는데 열심히 리팩터링해서 76줄까지 줄였다. 이럴때 참 뿌듯하다.

 

struct info {
    int idx, r, c, s;
};

각 회전 연산을 저장할 구조체다. r, c, s말고 있는 idx가 뭔가 싶을텐데 순열을 위해 넣은 것이다.

 

    cin >> N >> M >> K;
    matrix.assign(N, vector<int>(M));
    vector<info> rot(K);
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < M; j++)
            cin >> matrix[i][j];
    }
    for (int i = 0; i < K; i++) {
        cin >> rot[i].r >> rot[i].c >> rot[i].s;
        rot[i].idx = i; //인덱스
    }

입력을 받는 부분이다.

 

    vector<vector<int>> tmp = matrix; //임시저장

각 순열마다 배열의 상태가 달라질테니 tmp에 초기 상태를 저장한다.

 

    do {
        for (int i = 0; i < K; i++) { //회전
            for (int j = 1; j <= rot[i].s; j++) //각 회전 연산에 대해 rot[i].s개의 정사각형을 회전하게 됨
                rotate(rot[i].r - j - 1, rot[i].c - j - 1, //시작점
                       rot[i].r + j - 1, rot[i].c + j - 1); //끝점
        }
        result = min(result, calcArr(N, M)); //최솟값 갱신
        matrix = tmp; //복구
    } while (next_permutation(rot.begin(), rot.end(),
                              [](const info &i1, const info &i2) { //idx에 대해 순열
                                  return i1.idx < i2.idx;
                              }));

그리고 순열을 돌린다.

 

    while (next_permutation(rot.begin(), rot.end(),
                              [](const info &i1, const info &i2) { //idx에 대해 순열
                                  return i1.idx < i2.idx;
                              }));

구조체의 idx에 대해 오름차순 정렬을 하기 위해 이렇게 했다.

 

        for (int i = 0; i < K; i++) { //회전
            for (int j = 1; j <= rot[i].s; j++) //각 회전 연산에 대해 rot[i].s개의 정사각형을 회전하게 됨
                rotate(rot[i].r - j - 1, rot[i].c - j - 1, //시작점
                       rot[i].r + j - 1, rot[i].c + j - 1); //끝점
        }

먼저 연산의 갯수인 K개에 대해서...그리고 각 연산의 s에 대해서 2중 루프를 돈다.

rotate 함수를 보자.

 

void rotate(int sr, int sc, int er, int ec) {
    queue<int> q;
    q.push(matrix[sr][sc]); //시작점 값
    int row = sr, col = sc + 1, d = 0; //시작점의 바로 오른쪽 좌표로 초기화
    do {
        q.push(matrix[row][col]); //값 저장
        matrix[row][col] = q.front(); //이전 위치의 값으로 업데이트
        q.pop();
        row += dir[d].first;
        col += dir[d].second;
        if ((row < sr) || (row > er) || (col < sc) || (col > ec)) { //범위를 벗어나면 위치 조정하고 방향 바꾸기
            row = row - dir[d].first + dir[(d + 1) % 4].first;
            col = col - dir[d].second + dir[(d + 1) % 4].second;
            d = (d + 1) % 4;
        }
    } while (row != sr || col != sc + 1); //원위치로 돌아올 때까지
}

rotate 함수는 회전할 정사각형의 왼쪽위 좌표와 오른쪽아래 좌표를 받는다.

먼저 왼쪽위 좌표의 값을 큐에 담고 그 오른쪽 좌표부터 살펴보며

 

1. 해당 위치의 값을 큐에 담는다.

2. 해당 위치의 값을 이전 값(q.front())로 갱신한다.

 

를 반복한다.

 

        if ((row < sr) || (row > er) || (col < sc) || (col > ec)) { //범위를 벗어나면 위치 조정하고 방향 바꾸기
            row = row - dir[d].first + dir[(d + 1) % 4].first;
            col = col - dir[d].second + dir[(d + 1) % 4].second;
            d = (d + 1) % 4;
        }

범위를 벗어날 경우 방향을 바꿔주었다.

 

        result = min(result, calcArr(N, M)); //최솟값 갱신
        matrix = tmp; //복구

그런식으로 회전 연산을 끝마치면 최솟값을 갱신한다.

 

int calcArr(int N, int M) { //배열의 값
    int result = 5001, tmp = 0;
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < M; j++) //행의 합
            tmp += matrix[i][j];
        result = min(result, tmp); //최솟값 갱신
        tmp = 0;
    }
    return result;
}

최솟값을 구하는 과정은 간단하다.

Comments