Codeforces Round #753 (Div. 3) A부터 D까지 업솔빙
falconlee236
·2022. 1. 7. 19:59
Codeforces Round #753 (Div. 3) A부터 D까지 업솔빙
이번 대회에는 DIV. 3답게 C번까지 풀었지만 D번은 못풀었는데, 내가 마지막에 제출한 틀린 코드에서 pair에 정렬 순서만 바꾸면 풀리는 문제라서 이제는 D번을 풀 수 있는 단계까지 눈앞에 이르렀다. E번 문제는 빡 구현문제인데 원래라면 업솔빙 했어야 하지만 codefore와 atcoder에서 내가 당장 배워야 하는 것은 dp, greedy, constructive, math, implementation, number theory 이기 때문에 pass했다. 이번 대회에서 배운 것은
- 규칙을 찾는 문제에서는 문제가 하라는 대로 일단 처음 10개항은 써보기
- 잘 모르겠어도 끝까지 포기하지 말기
- 접근법이 잘못된 것 같으면 과감히 방향을 틀기
A. Linear Keyboard (*800)
접기/펼치기
문제 설명
26개 키로 구성되어있는 키보드가 주어진다. 키는 한줄에 특정한 순서로 일렬로 늘여있고 각 키는 모두 서로 다른 알파벳 소문자로 이루어져 있다. 우리는 이 키보드로 단어 $s$를 쳐야한다.
이 단어를 치기 위해서는 각각의 문자를 하나씩 순서대로 입력해야 한다. 각 문자를 입력하기 위해서는 그 문자의 정확한 위치로 손을 움직이고 눌러야 한다.
키 사이를 움직이는 것은 이 키들 사이에 있는 위치 차이의 절대값만큼 시간이 걸린다. 단어의 첫번째 문자를 입력할 때는 시간이 걸리지 않는다.
단어 $s$를 입력하기 위해서 걸리는 총 시간을 구하시오.
문제 해설
문제에서 주어진 키의 위치를 찾아내서 각 키 사이의 절댓값을 출력하면 되는 문제이다. 이 문제에서 핵심은 주어진 키의 위치를 찾는 것이다. 문제에서는 std::find를 사용해서 찾으라고 하는데, 다른 사람의 코드를 보니 결국 소문자 a부터 z는 아스키코드 128안에 다 들어가기 때문에 배열을 128개 선언해서 문자 자체를 index로 하는 위치 배열을 만들어서 푸는 사람이 있었고 이 사람의 방식이 좋은것 같아서 공부한다.
정답 코드
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
int alpha[128];
int main() {
ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int t; cin >> t;
while(t--){
string s; cin >> s;
for(int i = 0; i < s.size(); i++) alpha[s[i]] = i;
string a; cin >> a;
int ans = 0;
for(int i = 0; i < a.size() - 1; i++) ans += abs(alpha[a[i + 1]] - alpha[a[i]]);
cout << ans << "\n";
}
return 0;
}
B. Odd Grasshopper (*900)
접기/펼치기
문제 설명
메뚜기는 수직선 좌표 $x_0$에 위치해있다.
메뚜기는 점프를 하는데 현재 위치해있는 좌표의 홀/짝에 의해서 방향이 결정된다. 만약 현재 메뚜기가 위치한 좌표가 홀수라면 오른쪽으로 점프하고 현재 위치한 좌표가 짝수라면 왼쪽으로 점프한다.
이 메뚜기는 정확히 $i$분 후에 거리 $i$만큼 점프를 한다. 만약 $3$분이 지나면 메뚜기가 점프한 총 거리는 $1 + 2 + 3 = 6$이 된다.
메뚜기가 정확히 $n$분이 지났을 때 메뚜기가 위치한 좌표값을 구하시오.
문제 해설
문제에서 주어지는 제약이 $(0 \le n \le 10^{14})$ 이므로 일반적인 for문을 사용해서는 풀 수 없다는 것을 알수 있고 결국 수학이나 규칙을 찾아서 한번에 계산해야하는 문제이다.
규칙을 찾기 위해서 가장 쉬운 방법은 바로 나열하기이다. 처음 위치를 0이라고 하면 첫 4분동안 점프를 하면 메뚜기의 위치는 $-1, 1, 4, 0$ 이 된다. 첫 8분동안 점프를 하면 메뚜기의 위치는 $-1, 1, 4, 0, -5, 1, 8, 0$ 이 된다. 4의 배수에 계속 0이 등장하는 것이 보이는가? 결국 메뚜기가 4의 배수만큼 점프를 한다면 최종적인 위치는 $0$ 이 된다는 규칙을 발견했다.
주어진 횟수 $n$에 가장 가까운 4의 배수를 구하면 이 수분 까지 점프를 하면 메뚜기의 위치는 $0$이 된다. 그 다음 숫자만큼 위에서 말한 규칙대로 점프를 진행하면 답을 구할 수 있다. $n$을 4로 나눈 나머지 값만큼 반복문을 실행할 텐데 최대 3번만 반복문을 실행하므로 절대 시간초과가 날 수가 없다.
정답 코드
#include <iostream>
#include <algorithm>
using namespace std;
int main() {
ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int t; cin >> t;
while(t--){
long long x0, n; cin >> x0 >> n;
for(long long i = (n / 4) * 4 + 1; i <= n; i++) x0 += (x0 & 1 ? i : -i);
cout << x0 << "\n";
}
return 0;
}
C. Minimum Extraction (*1000)
접기/펼치기
문제 설명
$n$개의 정수로 이루어진 배열 $a$가 주어진다.
$a$의 길이가 $1$보다 크다면 minimum extraction 이라는 연산을 할 수 있다.
- 배열에서 가장 작은 숫자 $m$을 선택한다. 만약 가장 작은 숫자가 여러개 있다면 아무거나 선택한다.
- 배열에서 $m$을 제거하고, 남은 모든 원소에 $m$을 뺀다.
따라서 각 연산을 계속 하면 배열의 길이는 결국 $1$이 될 것이다.
우리가 찾아야 할 것은 minimum extraction 연산을 계속 하면서 나오는 숫자 $m$의 최댓값을 구하는 것이다.
문제 해설
이 문제도 규칙을 찾으면 쉽게 풀 수 있다. 규칙을 찾으려면 문제에서 말하는 minimum extraction 연산을 직접 수행해 봐야한다.
배열 $a$를 $a = [a_0, a_1, a_2, a_3]$ 이라고 해보자. 일단 이 배열에서 가장 작은 값을 찾아야 하는데 당장 떠오르는 것은 std::min_element 함수이다. 하지만 이 함수는 시간 복잡도가 $O(n)$ 이므로 $n \le 2 \cdot 10^5$ 일때 반드시 시간초과가 난다. 그렇다면 이 배열을 오름차순으로 정렬하면 어떨까? 맨 앞에 있는 원소가 최솟값이 되고, 뒤에 있는 나머지 원소에 최솟값을 빼고 맨 앞에 있는 원소를 무시해도 두번째 위치한 원소가 다음 연산의 최솟값이 계속 된다. 따라서 정렬을 하면 이 문제를 시간제한에 걸리지 않게 풀 수 있을 것이다.
- 첫번째 연산을 해보자. 가장 작은 수는 $a_0$이고 뺀 결과 배열은 $[a_1 - a_0, a_2 - a_0, a_3 - a_0]$ 이다.
- 두번째 연산을 해보자. 가장 작은 수는 $a_1 - a_0$이고 뺀 결과 배열은 $[a_2 - a_0 - (a_1 - a_0), a_3 - a_0 - (a_1 - a_0)]$ 이다.
- 세번째 연산을 해보자. 가장 작은 수는 $a_2 - a_0 - (a_1 - a_0) = a_2 - a_1$이다. 여기서 부터 뭔가 느낌이 이상하다. 뺀 결과 배열은 $[a_3 - a_0 - (a_1 - a_0) - (a_2 - a_1)]$ 이고 이 값은 $a_3 - a_2$ 이다.
즉 그냥 최솟값 $m$은 $a_i - a_{i - 1}$이다. 따라서 배열을 정렬한 다음에 저 값중 max값을 구하면 답이 나온다.
정답 코드
#include <iostream>
#include <algorithm>
using namespace std;
int main() {
ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int t; cin >> t;
while(t--){
int n; cin >> n;
int a[n];
for(auto& x : a) cin >> x;
sort(a, a + n);
int ans = a[0];
for(int i = 1; i < n; i++) ans = max(ans, a[i] - a[i - 1]);
cout << ans << "\n";
}
return 0;
}
D. Blue-Red Permutation (*1300)
접기/펼치기
문제 설명
길이가 $n$인 배열 $a$가 주어진다. 배열의 각 원소는 서로 같을 수 있다.
배열의 각 원소는 파란색 혹은 빨간색으로 칠해져 있다. 배열에 색깔이 칠해져 있지 않은 원소는 없다. 아래에 있는 두 연산 중 하나의 연산을 각 단계마다 사용할 수 있다.
- 아무 파란색으로 색칠된 원소를 선택해서 값을 $1$ 내린다.
- 아무 빨간색으로 색칠된 원소를 선택해서 값을 $1$ 올린다.
만약 한 원소가 모두 빨간색, 혹은 모두 파란색으로 색칠되어 있다면 나머지 한 연산은 사용할 수 없을 것이다.
우리는 위에 있는 연산을 0번 혹은 그 이상 수행해서 배열의 결과 값이 $1$부터 $n$ 까지 순열이 될 수 있는지 없는지 판독해야 한다.
문제 해설
문제에서 요구하는 정해와 거의 일치하지만 pair vector의 정렬 순서 차이가 문제의 AC, REJECT 여부를 만든 것 같다.
이 문제에서 핵심은 파란색과 빨간색으로 색칠된 원소의 특징을 알아내야 하는 것이다. 파란색으로 색칠된 원소는 그 값을 넘어서는 절대 만들 수 없다. 반대로 빨간색으로 색칠된 원소는 그 값보다 작은 값으로는 절대 될 수 없다. 만약에 우리가 이 배열을 색깔값과 원소값을 한번에 묶어서 정렬을 한다면 원소 값이 오름차순으로 배열될 것이다.
파란색으로 색칠된 값은 그 값보다 작은 수를 모두 만들 수 있기 때문에 오름차순 중에서 앞에 배치하면 더 많이 사용할 수 있을 것이다. 반대로 빨간색으로 색칠된 값은 그 값보다 큰 수를 모두 만들 수 있기 때문에 오름차순 중에서 뒤에 배치하면 더 많이 사용할 수 있을 것이다. 즉 작은 값이 만들어 질수 있는 파란색 원소를 앞에 배치하고 그 원소중에서 오름차순으로 배치한다면 차례대로 그 값이 $1$부터 $n$이 될 수 있는지 검사하고 하나라도 조건에 안맞는다면 false를 출력하고 break를 하면 된다.
만약 $2$ 가 나와야 하는데 빨간색 $3$이 등장한다면? 절대 $2$를 만들 수 없다. 하지만 파란색 $5$가 등장한다면 만들 수 있다. 우리가 오름차순으로 배열을 정렬했기 때문에 자신보다 작은 값만 만들 수 있는 파란색을 먼저 사용해야 뒤에 나오는 큰 수를 빨간색 원소를 이용해서 만들 수 있다. 그래서 우리가 색깔로 먼저 정렬을 한 이유이다.
나는 처음 문제를 풀때 원소값을 우선 순위로 정렬을 했더니 빨간색, 파란색이 모두 섞여버려서 문제를 못풀었다. 진짜 조금만 남은 것 같다. 뉴비 탈출!!
정답 코드
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main() {
ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int t; cin >> t;
while(t--){
int n; cin >> n;
int a[n];
for(auto& x : a) cin >> x;
vector<pair<char, int>> v;
for(int i = 0; i < n; i++){
char c; cin >> c;
v.push_back({c, a[i]});
}
sort(v.begin(), v.end());
bool flag = true;
for(int i = 0; i < n; i++){
if(v[i].first == 'B' && v[i].second < i + 1) flag = false;
if(v[i].first == 'R' && v[i].second > i + 1) flag = false;
}
cout << (flag ? "yes" : "no") << "\n";
}
return 0;
}
'알고리즘 > codeforces' 카테고리의 다른 글
Educational Codeforces Round 117 A부터 D까지 업솔빙 (0) | 2022.01.16 |
---|---|
Codeforces Round #754 (Div. 2) A부터 C까지 업솔빙 (0) | 2022.01.11 |
Codeforces Round #752 (Div. 2) A부터 D까지 업솔빙 (0) | 2022.01.02 |
Codeforces Round #737 (Div. 2) A부터 C까지 업솔빙 (0) | 2021.12.30 |
Educational Codeforces Round 112-C. Coin Rows (0) | 2021.12.25 |