
AtCoder Beginner Contest 204 - A부터 D까지 업솔빙
falconlee236
·2021. 11. 27. 09:39
AtCoder Beginner Contest 204 A부터 D까지 업솔빙
atcoder의 문제 수준에 갈수록 놀라면서 문제를 풀고 있다. 확실히 플랫폼이 달라서 그런지 출제되는 문제 유형도 확연하다고 느낀다. 하지만 여기서 배운 방식을 codeforces에서 쓸 수 있을 것 같다는 생각이다. 근데 codeforces에서 푼 문제는 글쎄? atcoder에서 쓸 수 있을까? 아직은 dp가 어렵기 때문에 그것은 지켜봐야할 문제인 것 같다.
이번 대회를 참여하면서 배운 것은
- 냅색 알고리즘의 변형 문제
문제 옆에 붙어있는 난이도는 Atcoder Problems 에서 추정한 것으로 작성했다는 것을 미리 알린다.
A - Rock-paper-scissors (*7)
접기/펼치기
문제 설명
사과, 오렌지, 포도는 가위바위보를 하고 있고, 이 결과를 비기고 싶다.
오렌지와 포도가 낸 패를 의미하는 문자
각각
사과가 내야하는 문자를 출력하자.
제약
- 각
, 는 과 과 로 이루어져 있다.
문제 해설
정답 코드
#include <iostream>
using namespace std;
int main() {
ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int a, b, c; cin >> a >> b >> c;
cout << 21 - (a + b + c);
return 0;
}
B - Nuts (*9)
접기/펼치기
문제 설명
나무
병건은 나무에서 다음과 같은 방식으로 땅콩을 수확하고 싶다.
- 나무에
개 혹은 그보다 작은 땅콩이 열리면 그 나무에 열린 땅콩을 수확하지 않는다. - 나무에
개 보다 많은 땅콩이 열리면 전체 땅콩을 수확하지만 개는 남긴다.
병건이가 수확해야하는 땅콩의 총 개수를 구하자.
제약
- 모든 입력은 정수로 이루어져 있다.
문제 해설
설명이 필요한가? 배열에 있는 모든 값을 순회하면서 그 값이 10보다 큰 경우에만 그 값에 10을 뺀 결과를 더하면 된다.
정답 코드
#include <iostream>
using namespace std;
int main() {
ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int n; cin >> n;
int ans = 0;
for(int i = 0; i < n; i++){
int num; cin >> num;
if(num > 10) ans += num - 10;
}
cout << ans;
return 0;
}
C - Tour (*629)
접기/펼치기
문제 설명
Atcoder 공화국에
퓨마는 어느 한 도시에서 여행을 시작하려고 계획하고 있다. 여행은 도로를 하나도 안쓰거나 수많은 도로를 사용할 수 있다.
퓨마의 여행의 시작과 끝으로 이루어진 도시의 집합쌍이 몇개나 있을까? 같은 집합이라도 다른 순서로 이루어져 있으면 다른 쌍이라고 구별한다.
제약
- $(A_i, B_i)은 구별된다.
- 모든 값은 정수로 주어진다.
문제 해설
처음 볼 때 그래프 문제라는 것을 알았는데, 문제를 읽어보고 그림을 그려보니까 생각보다 간단한 문제라는 것을 알았다. 이 문제를 풀면서 출제자가 물어보고 싶은게 뭔지 알았다.
너 DFS는 아니? 이 질문과 동의어 였기 때문에 그냥 나는 쉽게 문제를 풀었다.
그래프 탐색인 DFS와 BFS모두 한곳을 출발점으로 해서 갈수 있는 모든 정점을 탐색하는 방식이기 때문에 한 노드에서 시작할 때 DFS 함수를 호출하는 횟수만 세줘도 문제에서 요구하는 정점 쌍을 다 구할 수 있다.
이 문제가 어렵게 느껴지는 사람은 그래프 탐색의 기초를 배우고 오자. 그러면 쉽게 풀 수 있다.
정답 코드
#include <iostream>
#include <vector>
using namespace std;
vector<int> g[2001];
vector<bool> visit;
int ans = 0;
void dfs(int x){
visit[x] = true;
ans++;
for(int i = 0; i < g[x].size(); i++){
int next = g[x][i];
if(!visit[next]) dfs(next);
}
}
int main() {
ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int n, m; cin >> n >> m;
while(m--){
int u, v; cin >> u >> v;
g[u].push_back(v);
}
for(int i = 1; i <= n; i++){
visit = vector<bool>(n + 1);
dfs(i);
}
cout << ans;
return 0;
}
D - Cooking (*832)
접기/펼치기
문제 설명
철수는
접시
만약 철수가 두 오븐을 사용한다면 모든
제약
문제 해설
다이나믹 프로그래밍 인지 감도 안잡혀서 업솔빙을 하면서 DP문제라는 것을 깨달았다. 결론 부터 말하자면 이 문제는 0-1 냅색 알고리즘을 사용해서 푼다. 그런데 이 알고리즘에는 DP로 푸는 방식도 있는데, 그리디한 방법으로 푸는 경우도 있었고 내가 처음에 이 문제를 만났을 때 그리디한 방법으로 풀었다. 하지만 이 방식은 테스트 케이스 조금만 생각해보면 답이 아니라는 것을 바로 알기 때문에 다른 방법으로 선회 했어야 한다. 하지만 나는 이 냅색 알고리즘이 아직도 헷갈리고 어려웠기 때문에 생각하기 쉽지 않았다. 계속 이와 비슷한 알고리즘을 사용하는 문제를 풀어보면서 숙달해야겠다.
이 문제가 왜 DP를 사용하는 문제일까? 꼼수를 써보면 일단 문제 조건이 그리디를 사용하기에는 너무 작다는 것이 첫번째 이유다. 그리디를 사용하려면 최대한 시간이 빡빡하게 나올 텐데 이 문제는 최대 경우가 100000으로 엄청 작다. 그리고 이 문제는 결국
계속 오븐이 1개 있다고 가정을 하는데, 오븐이 1개있다고 할 때 다른 오븐에 넣는 시간은 상관 없다. 왜냐하면 첫번째 오븐이 요리를 하는데 사용하는 총 시간이
0-1 냅색 알고리즘은 DP를 이렇게 정의한다.
= 요리를 번째 까지 고려했을 때 분이 걸릴 수 있는가? true or false
그리고 이 알고리즘에서
반면에
만약
하지만
마지막으로 모든 요리를 수행했을 때 가능한 시간이
정답 코드
#include <iostream>
#include <numeric>
using namespace std;
int main() {
ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int n; cin >> n;
int arr[n];
for(int& i : arr) cin >> i;
bool dp[n + 1][100001] = {};
dp[0][0] = true;
int sum = accumulate(arr, arr + n, 0);
for(int i = 0; i < n; i++){
for(int j = 0; j <= 100000; j++){
if(j >= arr[i]) dp[i + 1][j] = dp[i][j] | dp[i][j - arr[i]];
else dp[i + 1][i] = dp[i][j];
}
}
int ans = 987654321;
for(int i = 0; i <= 100000; i++){
if(dp[n][i]) ans = min(ans, max(i, sum - i));
}
cout << ans;
return 0;
}
'알고리즘 > atcoder' 카테고리의 다른 글
AtCoder Beginner Contest 206 - A부터 D까지 업솔빙 (0) | 2021.12.19 |
---|---|
AtCoder Beginner Contest 205 - A부터 D까지 업솔빙 (0) | 2021.12.16 |
AtCoder Beginner Contest 202 - A부터 D까지 업솔빙 (0) | 2021.11.22 |
AtCoder Beginner Contest 201 - A부터 C까지 업솔빙 (0) | 2021.11.20 |
AtCoder Beginner Contest 200 - A부터 C까지 업솔빙 (0) | 2021.11.15 |