문제 설명
- "U X": 현재 선택된 행에서 X칸 위에 있는 행을 선택합니다.
- "D X": 현재 선택된 행에서 X칸 아래에 있는 행을 선택합니다.
- "C" : 현재 선택된 행을 삭제한 후, 바로 아래 행을 선택합니다. 단, 삭제된 행이 가장 마지막 행인 경우 바로 윗 행을 선택합니다.
- "Z" : 가장 최근에 삭제된 행을 원래대로 복구합니다. 단, 현재 선택된 행은 바뀌지 않습니다.
https://school.programmers.co.kr/learn/courses/30/lessons/81303
프로그래머스
코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.
programmers.co.kr
내 풀이 과정
첫 번째 시도
처음에 생각한 방법은 삭제되었는 지를 체크하는 bool 배열을 사용하여, 정확한 값을 저장하려고 하였다.
하지만 그렇게 풀기에는 너무 쉬워서 링크 리스트로 구현하면 좀 더 문제가 의도한 바에 일치하지 않나라는 생각을 하였다.
그래서 Record라는 배열에 현재 값, 이전 값, 다음 값을 저장 후 4가지 명령어에 따라 작동하도록 하였다.
첫 번째 코드
#include <string>
#include <vector>
#include <stack>
using namespace std;
const int max_size = 1000010;
struct Record {
int val;
int prev;
int next;
};
Record record[max_size];
stack <Record> deleted;
int now_record, record_count;
void setting(int n) {
for(int i = 0; i < n; ++i) {
record[i].val = i;
record[i].prev = (i == 0 ? i : i - 1);
record[i].next = (i == n - 1 ? i : i + 1);
}
}
void do_up(int n) {
while(n--) now_record = record[now_record].prev;
}
void do_down(int n) {
while(n--) now_record = record[now_record].next;
}
void do_delete() {
int prev = record[now_record].prev;
int next = record[now_record].next;
deleted.push(record[now_record]);
record[prev].next = next;
record[next].prev = prev;
now_record = (next != record_count - 1 ? next : prev);
}
void do_recover() {
Record target = deleted.top();
deleted.pop();
int val = target.val;
int prev = target.prev;
int next = target.next;
record[prev].next = val;
record[next].prev = val;
}
void do_cmd(string cmd) {
if(cmd.at(0) == 'U') do_up(stoi(cmd.substr(2)));
if(cmd.at(0) == 'D') do_down(stoi(cmd.substr(2)));
if(cmd.at(0) == 'C') do_delete();
if(cmd.at(0) == 'Z') do_recover();
}
string solution(int n, int k, vector<string> cmd) {
string answer = "";
now_record = k;
record_count = n;
for(int i = 0; i < n; ++i) answer += "O";
setting(n);
int cmd_size = cmd.size();
for(int i = 0; i < cmd_size; ++i) do_cmd(cmd.at(i));
while(!deleted.empty()) {
Record target = deleted.top();
deleted.pop();
answer[target.val] = 'X';
}
return answer;
}
첫 번째 결과
그런데 틀렸다.
뭐가 틀렸는 지 고민하고 새로운 테스트 케이스를 넣어보았는데..
두 번째 시도
기존 코드에서는 0번째 record의 prev와 n - 1번째 record의 next가 각각 0과 n - 1로 저장하게 하였다.
하지만 이렇게 하다보니 0번째나 n - 1번째 record를 삭제하는 명령어가 진행되면, 인접한 record에 그대로 저장이 되는 현상이 발생하였다.
그래서 이를 해결하기 위해 out_of_range라는 상수를 만들어, 끝에 위치하는 record에 저장하였다.
뿐만 아니라, 명령어를 수행 후, 인접한 record의 prev와 next를 갱신할 때, out_of_range값이 포함되어 있다면 따로 갱신하지 않도록 하였다.
두 번째 코드
#include <string>
#include <vector>
#include <stack>
using namespace std;
const int max_size = 1000010;
const int out_of_range = 987654321;
struct Record {
int val;
int prev;
int next;
};
Record record[max_size];
stack <Record> deleted;
int now_record;
void setting(int n) {
for(int i = 0; i < n; ++i) {
record[i].val = i;
record[i].prev = (i == 0 ? out_of_range : i - 1);
record[i].next = (i == n - 1 ? out_of_range : i + 1);
}
}
void do_up(int n) {
while(n--) now_record = record[now_record].prev;
}
void do_down(int n) {
while(n--) now_record = record[now_record].next;
}
void do_delete() {
int prev = record[now_record].prev;
int next = record[now_record].next;
deleted.push(record[now_record]);
if(prev != out_of_range) record[prev].next = next;
if(next != out_of_range) record[next].prev = prev;
now_record = (next != out_of_range ? next : prev);
}
void do_recover() {
Record target = deleted.top();
deleted.pop();
int val = target.val;
int prev = target.prev;
int next = target.next;
if(prev != out_of_range) record[prev].next = val;
if(next != out_of_range) record[next].prev = val;
}
void do_cmd(string cmd) {
if(cmd.at(0) == 'U') do_up(stoi(cmd.substr(2)));
if(cmd.at(0) == 'D') do_down(stoi(cmd.substr(2)));
if(cmd.at(0) == 'C') do_delete();
if(cmd.at(0) == 'Z') do_recover();
}
string solution(int n, int k, vector<string> cmd) {
string answer = "";
now_record = k;
for(int i = 0; i < n; ++i) answer += "O";
setting(n);
int cmd_size = cmd.size();
for(int i = 0; i < cmd_size; ++i) do_cmd(cmd.at(i));
while(!deleted.empty()) {
Record target = deleted.top();
deleted.pop();
answer[target.val] = 'X';
}
return answer;
}
두 번째 결과
찢었다
다른 사람 코드
다른 분들도 거의 모두 링크 리스트를 사용하여 풀었다.
함수 분리, 세세한 구현 사항들은 달랐지만, 로직 자체는 내 시도와 매우 흡사하였다.
그 중 개인적으로 더 깔끔하다 싶었던 구현 방법이 있었는데
typedef struct Node {
int row;
bool live;
Node *next;
Node *prev;
} Node;
Node nodes[1000000], *now;
바로 이 코드였다.
나는 현재 몇 번째 record를 다루고 있는 지를 now_count라는 변수에 저장하였는 데, 이 분은 *now라는 포인터를 사용하였다.
확실히 내 코드보다는 좀 더 직관적이고, c++을 더 잘 활용하신 것 같다.
'알고리즘 > 프로그래머스' 카테고리의 다른 글
[C++] Programmers 시험장 나누기 (0) | 2023.09.12 |
---|---|
[C++] Programmers 미로 탈출 (0) | 2023.08.24 |
[C++] Programmers 거리두기 확인하기 (0) | 2023.08.19 |
[C++] Programmers 숫자 문자열과 영단어 (0) | 2023.08.18 |