[C++] 응원단 (백준 28300번)
https://www.acmicpc.net/problem/28300
우선 이 문제의 포인트는 행이동/열이동과 스왑 연산을 분리하여 각각 처리해주는 것이다.
스왑 연산은 어떤 수와 어떤 수를 스왑하는지만 잘 저장해둔다면 어느 순서에서 하든 상관없기 때문에, 일단 무시하고 마지막에 한번에 처리해주도록 하자.
그렇다면 지금은 행이동/열이동만 순서대로 보면서 처리해주겠다. 이것들은 순서가 중요하기 때문에 한번에 모아서 처리하거나 하기 어렵다. 그렇다고 n*n 배열에서 실제로 행이동과 열이동을 매번 시켜주면 많은 시간이 걸릴 것이다.
잘 생각해보면 이렇게 행이동/열이동으로는 저 4개의 수끼리의 위치는 절대 바뀔 수 없다. 행이동과 열이동은 무조건 짝수 행/열 또는 홀수 행/열에 대해서만 이루어지기 때문이다.
그렇다는 것은 왼쪽 위 4개의 수(초록색 사각형 안에 있는 4개의 수)의 위치만 잘 기록해두면 마지막에 행이동/열이동이 모두 끝난 후에 나머지 수들도 채워줄 수 있다는 것이다. 행이동과 열이동은 이렇게 처리하면 된다.
이제 남은 건 스왑 연산이다. 다시 리마인드하자면, 스왑 연산은 중간 중간에 있을 수 있지만 우리는 잘 기록해두었다가 맨 마지막에 몰아서 처리하려고 하는 것이다.
스왑 연산이 원래부터 맨 마지막에 있었다면? 그냥 스왑해주면 된다.
그런데 스왑 연산이 중간에 있었다면? 그 이후에 있었던 행이동/열이동을 반영하여 스왑해주어야 하는 것이다.
즉, 해당 스왑 연산을 마지막 단계에서 잘 처리해주려면 해당 스왑 연산 이후에 행이동과 열이동이 어떻게 이루어졌는지만 잘 기록해두면 되는 것이다.
이를 위해서는 역추적을 하면 된다. 뒤에서부터 보면서 행이동과 열이동이 어떻게 이루어졌는지 기록하다가 스왑 연산을 만나면 지금까지 기록해뒀던 행이동/열이동 연산들을 반영해서 스왑 연산을 수정해준다. 맨 앞에 도달하면 다시 내가 수정해서 저장해둔 스왑 연산들을 읽으면서 그대로 스왑해주면 된다.
행이동/열이동 연산들을 기록할 때는, 홀수행+홀수열 원소, 홀수행+짝수열 원소, 짝수행+홀수열 원소, 짝수행+짝수열 원소로 나누어서 각각의 이동량을 누적해주면 된다. 주의할 점은 마지막에 수정한 스왑 연산들을 처리해줄 때는 다시 앞에서부터 읽으면서 스왑해주어야 한다는 점이다.
#include <iostream>
#include <vector>
#include <tuple>
using namespace std;
int main() {
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int n, q;
cin >> n >> q;
vector<pair<int, int>> a(4);
vector<tuple<string, int, int, int, int>> query;
// 4개의 원소가 각 행이동과 열이동으로 어느 위치로 갔는지 기록
// 쿼리도 전부 저장해둠 (이따가 역추적 시 사용)
a[0] = make_pair(1, 1);
a[1] = make_pair(1, 2);
a[2] = make_pair(2, 1);
a[3] = make_pair(2, 2);
for (int i = 0; i < q; i++) {
string op;
cin >> op;
if (op == "RO") {
for (int i = 0; i < 4; i++) {
if (a[i].first % 2 == 1) a[i].second = a[i].second % n + 1;
}
query.push_back({op, 0, 0, 0, 0});
} else if (op == "RE") {
for (int i = 0; i < 4; i++) {
if (a[i].first % 2 == 0) a[i].second = a[i].second % n + 1;
}
query.push_back({op, 0, 0, 0, 0});
} else if (op == "CO") {
for (int i = 0; i < 4; i++) {
if (a[i].second % 2 == 1) a[i].first = a[i].first % n + 1;
}
query.push_back({op, 0, 0, 0, 0});
} else if (op == "CE") {
for (int i = 0; i < 4; i++) {
if (a[i].second % 2 == 0) a[i].first = a[i].first % n + 1;
}
query.push_back({op, 0, 0, 0, 0});
} else if (op == "S") {
int r1, c1, r2, c2;
cin >> r1 >> c1 >> r2 >> c2;
query.push_back({op, r1, c1, r2, c2});
}
}
// 4개의 원소의 위치를 통해 다른 원소들의 위치도 결정
vector<vector<int>> ans(n + 1, vector<int>(n + 1, -1));
for (int i = 0; i < n; i += 2) {
for (int j = 0; j < n; j += 2) {
ans[(a[0].first + i - 1) % n + 1][(a[0].second + j - 1) % n + 1] = n * i + 1 + j;
}
}
for (int i = 0; i < n; i += 2) {
for (int j = 0; j < n; j += 2) {
ans[(a[1].first + i - 1) % n + 1][(a[1].second + j - 1) % n + 1] = n * i + 2 + j;
}
}
for (int i = 0; i < n; i += 2) {
for (int j = 0; j < n; j += 2) {
ans[(a[2].first + i - 1) % n + 1][(a[2].second + j - 1) % n + 1] = n * i + n + 1 + j;
}
}
for (int i = 0; i < n; i += 2) {
for (int j = 0; j < n; j += 2) {
ans[(a[3].first + i - 1) % n + 1][(a[3].second + j - 1) % n + 1] = n * i + n + 2 + j;
}
}
// 역추적하면서 각 스왑 연산 이후에 어떻게 원소들이 이동했는지 파악해서 스왑에 저장해둠
vector<pair<int, int>> pos(4); // <행 이동량, 열 이동량>
vector<tuple<int, int, int, int>> swaps;
pos[0] = {0, 0}; // OO
pos[1] = {0, 0}; // OE
pos[2] = {0, 0}; // EO
pos[3] = {0, 0}; // EE
for (int i = q - 1; i >= 0; i--) {
auto [op, x1, y1, x2, y2] = query[i];
if (op == "RO") {
pos[0].second++;
pos[1].second++;
swap(pos[0], pos[1]); // 홀짝이 바뀌었으니 swap까지 해주기
} else if (op == "RE") {
pos[2].second++;
pos[3].second++;
swap(pos[2], pos[3]); // 홀짝이 바뀌었으니 swap까지 해주기
} else if (op == "CO") {
pos[0].first++;
pos[2].first++;
swap(pos[0], pos[2]); // 홀짝이 바뀌었으니 swap까지 해주기
} else if (op == "CE") {
pos[1].first++;
pos[3].first++;
swap(pos[1], pos[3]); // 홀짝이 바뀌었으니 swap까지 해주기
} else if (op == "S") {
// 현재 스왑 연산 이후에 있던 연산들 모두 반영해서 swaps에 넣어주기
// (x1, y1) 계산
if (x1 % 2 == 1 && y1 % 2 == 1) { // OO
x1 = (x1 + pos[0].first - 1) % n + 1;
y1 = (y1 + pos[0].second - 1) % n + 1;
} else if (x1 % 2 == 1 && y1 % 2 == 0) { // OE
x1 = (x1 + pos[1].first - 1) % n + 1;
y1 = (y1 + pos[1].second - 1) % n + 1;
} else if (x1 % 2 == 0 && y1 % 2 == 1) { // EO
x1 = (x1 + pos[2].first - 1) % n + 1;
y1 = (y1 + pos[2].second - 1) % n + 1;
} else if (x1 % 2 == 0 && y1 % 2 == 0) { // EE
x1 = (x1 + pos[3].first - 1) % n + 1;
y1 = (y1 + pos[3].second - 1) % n + 1;
}
// (x2, y2) 계산
if (x2 % 2 == 1 && y2 % 2 == 1) { // OO
x2 = (x2 + pos[0].first - 1) % n + 1;
y2 = (y2 + pos[0].second - 1) % n + 1;
} else if (x2 % 2 == 1 && y2 % 2 == 0) { // OE
x2 = (x2 + pos[1].first - 1) % n + 1;
y2 = (y2 + pos[1].second - 1) % n + 1;
} else if (x2 % 2 == 0 && y2 % 2 == 1) { // EO
x2 = (x2 + pos[2].first - 1) % n + 1;
y2 = (y2 + pos[2].second - 1) % n + 1;
} else if (x2 % 2 == 0 && y2 % 2 == 0) { // EE
x2 = (x2 + pos[3].first - 1) % n + 1;
y2 = (y2 + pos[3].second - 1) % n + 1;
}
// swaps에 넣어주기
swaps.push_back({x1, y1, x2, y2});
}
}
// 아까 계산해둔대로 실제로 스왑해주기
int sn = swaps.size();
for (int i = sn - 1; i >= 0; i--) {
auto [x1, y1, x2, y2] = swaps[i];
swap(ans[x1][y1], ans[x2][y2]);
}
for (int i = 1; i < n + 1; i++) {
for (int j = 1; j < n + 1; j++) {
cout << ans[i][j] << ' ';
}
cout << '\n';
}
}
(AC)