Codeforces Round #754 (Div. 2) A부터 C까지 업솔빙
falconlee236
·2022. 1. 11. 22:44
Codeforces Round #754 (Div. 2) A부터 C까지 업솔빙
A번문제만 푼 대회 B번 문제는 계속 봐도 해설이 안떠올라서 그나마 만만해 보이는 C번 문제로 넘어갔는데 경우의수 1가지를 못찾아서 못풀었다. *1400난이도 치고는 경우의수 7가지만 찾으면 되는 쉬운 문제 였는데, 못풀어서 아쉬웠다. B번 문제는 이전에 풀었던 C. Rings 랑 비슷한 문제라서 더더욱 폿풀어서 아쉬운 문제였다. 계속 훈련을 하면 언젠가는 단단해 지겠지..
이번 대회에서 배운 것은
- 문제에서 제공하는 예제 풀이에 너무 몰입하여 생각하지 않기.
- A, B, C는 생각보다 쉬운 문제다!
- 끝까지 경우의수 생각하면서 풀기
A. A.M. Deviation (*800)
접기/펼치기
문제 설명
세 숫자 $a_1, a_2, a_3$ 의 산술평균은 다음과 같이 정의한다.
- $d(a_1, a_2, a_3) = |a_1 + a_3 - 2 \cdot a_2|$
우리가 구해야 할것은 $d(a_1, a_2, a_3)$의 값을 최소화 하는 것이다. 이 값을 최소화하기 위해서 우리는 다음 연산을 몇번이고 수행할 수 있다. (수행하지 않을 수도 있다.)
- ${1, 2, 3}$ 에서 $i \neq j$ 를 만족하는 $i, j$를 선택하고 $a_i$ 값을 1 증가시키고 $a_j$값을 1 감소시킨다.
위에서 말한 연산을 몇번이고 수행해서 $d(a_1, a_2, a_3)$ 의 최솟값을 구하자.
문제 해설
일단 $a_1, a_3$의 값을 각각 1씩 늘리고 1씩 줄이면 값이 변하지 않는다. 따라서 $a_2$ 값을 1 줄이거나 반드시 1 늘려야 한다. 그렇다면 $|a_1 + a_3 - 2 \cdot a_2 - 3\alpha|$ 식이 나온다. $\alpha$ 값은 정수이다.
반드시 원래 값에서 3씩 더해지거나 빼지기 때문에 다음 성질이 성립한다.
- 일단 $a_1 + a_3 - 2 \cdot a_2$ 값이 3으로 나누어 떨어진다면 저 값은 반드시 0이 된다.
- $a_1 + a_3 - 2 \cdot a_2$ 값이 3으로 나누어 떨어지지 않는다면 $3\alpha$를 더하거나 빼서 절댓값이 1이 되게 만들 수 있다. 만약 저 값이 $5$라면 이 수에서 $-6$을 더해서 $|-1| = 1$을 만들 수 있고, 저 값이 $4$라면 이 수에서 $-3$을 더해서 $1$을 만들 수 있다.
따라서 $a_1 + a_3 - 2 \cdot a_2$이 값이 3으로 나누어 떨어진다면 답은 0, 아니면 답은 1이다.
정답 코드
#include <iostream>
using namespace std;
int main() {
ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int t; cin >> t;
while(t--){
int a, b, c; cin >> a >> b >> c;
cout << ((a + c - 2 * b) % 3 ? 1 : 0) << "\n";
}
return 0;
}
B. Reverse Sort (*1000)
접기/펼치기
문제 설명
길이가 $n$인 이진수 문자열 $s$를 오름차순으로 정렬하고 싶다. 정렬을 할 때 다음 연산을 사용할 수 있다.
- $1 \le k \le n$ 인 $k$를 선택하고 $1 \le i_1 < i_2 < ... < i_k \le n$ 인 index를 가지고 있는 문자 $s_{i_1} \ge s_{i_2} \ge ... \ge s_{i_k}$ 를 선택한다.
- 이 문자들을 모두 뒤집고 원래 위치에 뒤집은 문자열을 넣는다.
오름차순이 아닌 문자열을 오름차순으로 정렬하기 위해서 필요한 최소 연산의 횟수를 찾아라.
문제 해설
대회에서는 어떻게 풀지?라고 생각하면서 Note나 Example처럼 해보면서 문제를 풀 방법을 고민하고 있었다. 내가 이 대회에서 배운 점에서 적어놓은 것 처럼 문제에서 제공하는 예제풀이에 너무 맹목적으로 생각하면 안된다는 것을 다시한번 상기시켜준 문제인 것 같다. 예제풀이대로 풀면 어짜피 정해가 아니기 때문에 풀이가 산으로 간다. ㅋㅋㅋㅋㅋㅋ
일단 이미 정렬되어 있는 경우는 연산이 필요가 없으므로 0을 출력해 주면 된다. 정렬이 되어 있는지 어떻게 판단을 할까? c++ STL에서는 이럴때 필요한 함수를 미리 구현해 두었다. algorithm 헤더에 있는 is_sorted()함수를 사용하면 쉽게 정렬이 되어 있는지 판단할 수 있다.
정렬이 안되어 있는 경우는 어떻게 할까? 결론부터 말하자면 반드시 연산 1번으로 오름차순으로 정렬할 수 있다. 원래 정렬이 안되어 있는 문자열을 $10100$이라고 하면 정렬이 되어있는 문자열은 $00011$이다. 그렇다면 정렬이 되어있는 문자열과 정렬이 되어있지 않은 문자열을 비교해서 정렬이 되어있는 문자열의 각 문자 위치에 있으면 안되는 문자를 모두 선택해서 뒤집으면 정렬이 된다.
1이 원래 정렬된 상태에서 있어야하는 위치는 $1$의 개수를 cnt라고 하면 $n - cnt + 1$부터 $n$까지이다. 그렇다면 0이 원래 정렬된 상태에 있어야 하는 위치는 $1$ 부터 $n - cnt$까지일 것이다. 각각 범위를 순회하면서 정렬된 문자열 기준으로 $1$이 있어야하는 위치에 $0$이 있는 경우, $0$이 있어야 하는 위치에 $1$이 있어야 하는 경우의 index를 모두 긁어모아서 출력하면 답이다.
정답 코드
#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;
char s[n];
int cnt = 0;
for(int i = 0; i < n; i++){
cin >> s[i];
if(s[i] == '1') cnt++;
}
if(is_sorted(s, s + n)){
cout << 0 << "\n";
continue;
}
vector<int> v;
for(int i = 0; i < n - cnt; i++){
if(s[i] != '0') v.push_back(i + 1);
}
for(int i = n - cnt; i < n; i++){
if(s[i] != '1') v.push_back(i + 1);
}
cout << 1 << "\n" << v.size() << " ";
for(auto x : v) cout << x << " ";
cout << "\n";
}
return 0;
}
C. Dominant Character (*1400)
접기/펼치기
문제 설명
문자 a, b, c로만 이루어진 길이가 $n$인 문자열 $s$가 있다.
다음 조건을 모두 만족하는 가장 작은 길이의 substring을 찾고 싶다.
- substring의 길이는 최소 2이상이다.
- substring에서 a의 개수는 c의 개수보다 커야한다.
- substring에서 a의 개수는 b의 개수보다 커야한다.
가장 작은 길이의 substring을 찾게 도와주자.
문제 해설
따지고보면 B번보다 쉬운 문제라고 생각한다. 단순히 문제의 조건을 만족하는 문자열의 경우의 수를 세보면 총 7가지 문자열 밖에 없다는 것을 알 수 있고, 찾기 어려운 경우는 $abbacca, accabba$이다. 이거 찾으면 문제는 그냥 쉽다. 길이가 작은 순으로 조건을 모두 만족시키는 문자열을 배열에 넣고 반복문을 돌려서 먼저 찾은 순서대로 답을 출력하고 끝내면 된다. 만약 저 7가지 경우가 아닌 경우 조건을 만족하는 substring은 반드시 존재하지 않기 때문에 -1을 출력하면 된다.
정답 코드
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
using namespace std;
int main() {
ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int t; cin >> t;
string a[7] = {"aa", "aba", "aca", "abca", "acba", "abbacca", "accabba"};
while(t--){
int n; cin >> n;
string s; cin >> s;
int ans = -1;
for(int i = 0; i < 7; i++){
if(s.find(a[i]) != -1){
ans = a[i].length();
break;
}
}
cout << ans << "\n";
}
return 0;
}
'알고리즘 > codeforces' 카테고리의 다른 글
Codeforces Round #757 (Div. 2) A부터 C까지 업솔빙 (0) | 2022.01.21 |
---|---|
Educational Codeforces Round 117 A부터 D까지 업솔빙 (0) | 2022.01.16 |
Codeforces Round #753 (Div. 3) A부터 D까지 업솔빙 (0) | 2022.01.07 |
Codeforces Round #752 (Div. 2) A부터 D까지 업솔빙 (0) | 2022.01.02 |
Codeforces Round #737 (Div. 2) A부터 C까지 업솔빙 (0) | 2021.12.30 |