AtCoder Beginner Contest 206 - A부터 D까지 업솔빙
falconlee236
·2021. 12. 19. 15:27
AtCoder Beginner Contest 206 A부터 D까지 업솔빙
진짜 아쉬웠던 대회, 내 수준은 엣코더를 6번째 하면서 D번까지 풀면 실력이 오른거고 D번을 못풀면 실력이 아직 재자리라고 느끼는데 이번에는 접근 방법은 다 생각했는데 구현 방식을 몰라서 못풀었다. 이런 작동을 하는 효율적인 알고리즘이 무엇일까? 라는 생각의 답을 찾기 위해서는 다양한 variation의 문제를 풀면서 경험을 쌓는 수밖에 없다.
이번 대회를 참여하면서 배운 것은
- Disjoint-Set(분리집합)의 find함수의 쓰임새
문제 옆에 붙어있는 난이도는 Atcoder Problems 에서 추정한 것으로 작성했다는 것을 미리 알린다.
A - Maxi-Buying (*5)
접기/펼치기
문제 설명
Atcoder 공화국에서는 소비세가 8%이다.
이 나라의 에너지 음료 상점은 에너지 음료를 세금을 제외하고 $N$원에 판다.
세금을 포함하면 가격은 $[1.08 \times N]$ 원이 된다. 이때 $[x]$는 $x$보다 크지 않은 정수 중에서 가장 큰 수를 의미한다.
만약 세금을 포함한 가격이 $206$원보다 작으면 "Yay!"를 출력하고, 가격이 같다면 "so-so$를 출력하고 가격이 크다면 ":("를 출력한다.
제약
- $0 \le N \le 300$
- 모든 입력은 정수이다.
문제 해설
$n \times 1.06$을 한 값을 int에 담으면 되는 문제이다. 주의해야 할점은 int * double은 double로 자동 type casting이 되기 때문에 cout을 하면 실수로 출력된다는 것이다.
정답 코드
#include <iostream>
using namespace std;
int main() {
ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int n; cin >> n;
int res = n * 1.08;
cout << (res < 206 ? "Yay!" : (res > 206 ? ":(" : "so-so"));
return 0;
}
B - Savings (*14)
접기/펼치기
문제 설명
진수는 빈 돼지저금통을 가지고 있다.
$i$번째 아침에 진수는 저금통에 $i$원을 넣는다. 즉 $1$번째날에는 1원을 넣고, $2$번째 날에는 2원을 넣는다.
각 밤마다 진수는 돈이 얼마나 있는지 확인한다.
각 날 밤에 진수가 돼지저금통을 확인했을 때 $N$원이거나 $N$원보다 크게 되는 날은 언제일까?
제약
- $1 \le N \le 10^9$
- 모든 입력은 정수로 이루어져 있다.
문제 해설
$1$부터 $n$까지 반복문을 돌려서 $res >= n$인 순간의 답을 구하거나, 등차수열이므로 $(i + 1) \times i / 2$ 의 값이 $n$보다 큰 $i$를 구하면 된다.
정답 코드
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main() {
ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int n; cin >> n;
int ans = 0, res = 0;
for(int i = 1; res < n; i++){
res += i;
ans++;
}
cout << ans;
return 0;
}
C - Swappable (*171)
접기/펼치기
문제 설명
$N$개 정수로 이루어진 배열 $A = (A_1, A_2, ..., A_N)$이 주어진다. 다음 모든 조건을 만족하는 순서쌍 $(i, j)$의 개수를 구하자.
- $1 \le i < j \le N$
- $A_i \neq A_j$
제약
- $2 \le N \le 3 \times 10^5$
- $1 \le A_i \le 10^9$
- 모든 값은 정수로 주어진다.
문제 해설
이전에 풀었던 ABC200-C와 아주 유사한 문제이다. 결국 $i < j$ 이기 때문에 전체에서 두 수를 선택만 하면 자동으로 순서가 보장되는 조합을 이용해서 문제를 풀면 된다.
전체 조합인 ${N}\mathrm{C}{2}$ 에서 각 숫자가 출현한 횟수에서 2를 선택하는 조합을 취한 모든 값을 빼면 된다. 만약 $2$가 3번 나왔고, $10000$이 10번 나왔다면 전체에서 - ${3}\mathrm{C}{2}$ - ${10}\mathrm{C}{2}$ 을 하면 된다.
이때 $A_i$의 값은 $10^9$ 까지이므로 배열의 한도를 초과하니 연관 자료형인 std::map을 사용한다.
정답 코드
#include <iostream>
#include <map>
using namespace std;
int main() {
ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);
long long n; cin >> n;
int arr[n];
map<int, long long> mp;
long long res = n * (n - 1) / 2;
for(int i = 0; i < n; i++){
int num; cin >> num;
mp[num]++;
}
for(auto x : mp) res -= (x.second * (x.second - 1) / 2LL);
cout << res;
return 0;
}
D - KAIBUNsyo (*879)
접기/펼치기
문제 설명
$N$개 정수로 이루어진 배열 $A = (A_1, A_2, ..., A_N)$이 주어진다. 우리는 아래에 있는 연산을 한번이상 수행하거나 수행하지 않을 수 있다. $A$를 펠린드롬으로 만들기 위해서 필요한 최소 연산의 개수는 무엇일까?
- 양수로 이루어진 순서쌍 $(x, y)$을 선택하고, $A$에 있는 모든 $x$를 $y$로 바꾼다.
우리는 $A_i = A_{N + 1 - i}$을 만족하는 모든 $i (1 \le i \le N)$ 이 존재할 때 $A$를 펠린드롬이라고 한다.
제약
- $1 \le N \le 2 \times 10^5$
- $1 \le A_i \le 2 \times 10^5$
- 모든 입력은 정수로 주어진다.
문제 해설
서로 양 끝부터 비교해서 양 끝이 같으면 바꿀 필요가 없다. 그런데 양 끝이 서로 다르면 두 수 중 하나는 무조건 바꿔야하는데, 이때 $A$에 있는 모든 수를 바꿔야 하기 때문에 $A$에 mapping되는 배열을 하나 가지고 있어서 최종적으로 변하는 원소를 가리키고 있으면 좋을것 같다고 생각했다. 이때 두 수 중 어느것을 바꿔도 답에 영향은 없다.
그렇기 때문에 이 문제를 $Disjoint-Set$ 자료구조에 있는 find 함수를 이용한다. disjoint-set 자료구조의 root배열은 각 원소가 가리키고 있는 최종적인 root를 가리키게 되는데, 이것을 서로 다른 수가 만났을 때 한 수가 다른 수의 부모로 들어가게 계속 갱신해준다면, find함수의 반환값이 최종적으로 변환되는 원소를 의미한다.
즉 두 배열의 원소가 나타났을 때 각 원소의 find를 한 반환값이 최종적으로 이 수가 변하게 된 수이기 때문에 이 값을 비교해서 서로 다르면 한 원소가 다른 한 원소의 부모로 들어가게 가리키면 된다. 그러면 다음번에 같은 상황이 나오면 이때는 서로 부모가 같이 때문에 서로 같은 값을 가지게 되서 펠린드롬 조건을 충족한다. 서로 다르면 답을 1 증가시키는 것도 잊지말자.
정답 코드
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int root[200001];
int find(int x){
return (root[x] == x ? x : root[x] = find(root[x]));
}
int main() {
ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int n; cin >> n;
for(int i = 0; i <= 200001; i++) root[i] = i;
int arr[n];
for(int& i : arr) cin >> i;
int ans = 0;
for(int i = 0; i < n / 2; i++){
int u = find(arr[i]), v = find(arr[n - i - 1]);
if(u == v) continue;
ans++;
root[u] = v;
}
cout << ans;
return 0;
}
'알고리즘 > atcoder' 카테고리의 다른 글
AtCoder Beginner Contest 209 - A부터 D까지 업솔빙 (0) | 2021.12.26 |
---|---|
AtCoder Beginner Contest 208 A부터 D까지 업솔빙 (0) | 2021.12.24 |
AtCoder Beginner Contest 205 - A부터 D까지 업솔빙 (0) | 2021.12.16 |
AtCoder Beginner Contest 204 - A부터 D까지 업솔빙 (0) | 2021.11.27 |
AtCoder Beginner Contest 202 - A부터 D까지 업솔빙 (0) | 2021.11.22 |