[2021 KAKAO BLIND RECRUITMENT] 카드 짝 맞추기


문제링크

[풀이]

숫자 쌍을 선택해서 해당 쌍으로 먼저 이동, 삭제 -> dfs 반복

숫자 쌍은 Permutation 해서 모든 조합을 다 dfs 해봐야 함 (1먼저 없애고, 2 먼저 없애는 것이 결과가 다를 수 있음)

#include <string>
#include <vector>
#include <deque>
#include <set>
#include <utility>
#include <algorithm>

#define MIN(a, b) ((a) > (b) ? (b) : (a))
#define INBOUND(r, c) (!((r) > 3 || (r) < 0 || (c) > 3 || (c) < 0))
using namespace std;

const int dir[4][2] = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} };
struct Pos {
    int r; int c; 
    Pos(int r, int c):r(r),c(c) {}
};

int _min = 0x7fffffff;
vector<vector<Pos>> positions(7);
vector<int> perm;
vector<vector<int>> gboard;

Pos get_next_position(int d, int r, int c, int ctrl_pressed) {
    if(ctrl_pressed) {
        while(true) {
            r += dir[d][0];
            c += dir[d][1];
            
            if(!INBOUND(r, c)){
                return Pos(r - dir[d][0], c - dir[d][1]);
            }
                        
            if(gboard[r][c] != 0) {
                return Pos(r, c);
            }
        }
        
    }else {
        int nr = r + dir[d][0];
        int nc = c + dir[d][1];
        if(!INBOUND(nr, nc)) 
            return Pos(r, c);
        
        return Pos(nr, nc);
    }
}

int get_distance(int r1, int c1, int r2, int c2) {
    deque<Pos> q;
    q.push_back(Pos(r1, c1));
    int distance = 0;
    while(!q.empty()) {
        
        deque<Pos> nextq;
        
        while(!q.empty()){
            Pos pos = q.front(); q.pop_front();
            if(pos.r == r2 && pos.c == c2) {
                return distance;
            }

            // 키 조작 : 방향키, 컨트롤+방향키, 엔터
            for(int d = 0 ; d < 8 ; d++) {
                Pos next = get_next_position(d / 2, pos.r, pos.c, d % 2); // 같은 방향으로, ctrl을 눌렀을때와 안눌렀을 때 2번 호출
                if(next.r != pos.r || next.c != pos.c){
                    for(Pos it : nextq) {
                        if(it.r == next.r && it.c == next.c) continue; // 이미 큐에 넣은 것일 경우
                    }
                    nextq.push_back(next);
                }
            }            
        }
        q = nextq;
        distance++;
    }
    return distance;
} 

void dfs(int cur_r, int cur_c, int depth, int score) {
    if(perm.size() == depth) {
        _min = MIN(_min, score);   
        return;
    }
    if(score > _min) return;
    
    int dist1 = 0, dist2 = 0;
    
    Pos first = positions[perm[depth]][0]; 
    Pos second = positions[perm[depth]][1];
    int card = gboard[first.r][first.c];
    
    dist1 += get_distance(cur_r, cur_c, first.r, first.c) + 1; // + 1 for "Enter"
    dist1 += get_distance(first.r, first.c, second.r, second.c) + 1;
    gboard[first.r][first.c] = gboard[second.r][second.c] = 0;
    dfs(second.r, second.c, depth + 1, score + dist1);
    gboard[first.r][first.c] = gboard[second.r][second.c] = card;
    
    dist2 += get_distance(cur_r, cur_c, second.r, second.c) + 1;
    dist2 += get_distance(second.r, second.c, first.r, first.c) + 1;
    gboard[first.r][first.c] = gboard[second.r][second.c] = 0;
    dfs(first.r, first.c, depth + 1, score + dist2);
    gboard[first.r][first.c] = gboard[second.r][second.c] = card;
}

int solution(vector<vector<int>> board, int r, int c) {
    gboard = board;
    
    set<int> p;
    for(int i = 0 ; i < 4 ; i++) {
        for(int j = 0 ; j < 4 ; j++) {
            if(board[i][j] != 0) {
                positions[board[i][j]].push_back(Pos(i, j));
                p.insert(board[i][j]);
            }
        }
    }

    for(auto it : p) perm.push_back(it);
    
    do {
        // arr[0] ~ arr[n-1] 순서대로 뒤집음
        dfs(r, c, 0, 0);
    }while(next_permutation(perm.begin(), perm.end()));
    
    return _min;
}

21.08.25 두 번째 풀이

  • getCoast(), getNextPoint() 효율 개선
#include <string>
#include <vector>
#include <map>
#include <queue>
#include <cstdio>
#include <algorithm>

using namespace std;
#define INBOUND(r, c) (r < 4 && r >= 0 && c < 4 && c >= 0)
#define MIN(a, b) ((a) < (b) ? (a) : (b))
const int dx[] = {-1, 1, 0, 0};
const int dy[] = {0, 0, -1, 1};

typedef struct Point {
    int row, col, coast;
    bool operator<(const Point &cmp) const {
        return coast > cmp.coast;
    }
}Point;

map<int, vector<pair<int,int>>> getCards(vector<vector<int>> board) {
    map<int, vector<pair<int, int>>> cards;
    for(int i = 0 ; i < board.size(); i++) {
        for(int j = 0 ; j < board[i].size() ; j++) {
            if(board[i][j]) {
                cards[board[i][j]].push_back({i, j});
            }
        }
    }
    return cards;
}

pair<int, int> getNextPoint(vector<vector<int>> board, int row, int col, int dir, bool ctrl) {
    int nr = row + dx[dir];
    int nc = col + dy[dir];
    if(!INBOUND(nr, nc))
        return {row, col};
    
    if(ctrl) {
        while(true) {
            if(board[nr][nc] != 0) 
                break;
            int tr = nr + dx[dir];
            int tc = nc + dy[dir];
            if(!INBOUND(tr, tc))
                break;
            nr = tr;
            nc = tc;
        }
    }
    return {nr, nc};
}

int getCoast(vector<vector<int>> board, int row, int col, int drow, int dcol) {
    priority_queue<Point> pq;
    pq.push({row, col, 0});
    vector<vector<int>> visit(4, vector<int>(4, 0));
    visit[row][col] = 1;
    while(!pq.empty()) {
        Point top = pq.top(); pq.pop();
        if(top.row == drow && top.col == dcol) 
            return top.coast;
        for(int i = 0 ; i < 8 ; i++) {
            pair<int, int> next = getNextPoint(board, top.row, top.col, i % 4, (i / 4 ? false : true));
            if(next.first == top.row && next.second == top.col) continue;
            if(visit[next.first][next.second] != 0) continue;
            visit[next.first][next.second] = 1;
            Point np = {next.first, next.second, top.coast + 1};
            pq.push(np);
        }
    }
    return 0;
}

int answer = 0x7fffffff;
map<int, vector<pair<int, int>>> cards;
void solve(vector<vector<int>> &board, vector<int> &seq, Point cur, int depth) {
    if(cur.coast >= answer) return;
    if(depth >= seq.size()) {
        answer = MIN(answer, cur.coast);
        return;
    }
    
    int card = seq[depth];
    int fRow = cards[card][0].first, fCol = cards[card][0].second;
    int sRow = cards[card][1].first, sCol = cards[card][1].second;
    // cards[card].first 먼저 뒤집고 second 다음에 뒤집
    int coast = 0;
    coast = getCoast(board, cur.row, cur.col, fRow, fCol);
    coast += 1; // first enter
    coast += getCoast(board, fRow, fCol, sRow, sCol);
    coast += 1; // second enter
    board[fRow][fCol] = board[sRow][sCol] = 0;
    solve(board, seq, {sRow, sCol, cur.coast + coast}, depth + 1);
    
    // 원상복구 후에 second 먼저 뒤집기
    coast = 0;
    board[fRow][fCol] = board[sRow][sCol] = card;
    coast = getCoast(board, cur.row, cur.col, sRow, sCol);
    coast += 1; // first enter
    coast += getCoast(board, sRow, sCol, fRow, fCol);
    coast += 1; // second enter
    board[fRow][fCol] = board[sRow][sCol] = 0;
    solve(board, seq, {fRow, fCol, cur.coast + coast}, depth + 1);  
    
    board[fRow][fCol] = board[sRow][sCol] = card; // 원상복구.. 꼭 해줘야 함..ㅜㅜ
}

int solution(vector<vector<int>> board, int r, int c) {
    cards = getCards(board);
    vector<int> keys;
    for(auto iter = cards.begin(); iter != cards.end() ; iter++)  
        keys.push_back(iter->first);
    do {
        vector<vector<int>> cp = board;
        solve(cp, keys, {r, c, 0}, 0);
    }while(next_permutation(keys.begin(), keys.end()));
    return answer;
}





© 2020.02. by blupine