- 느리게 갱신되는 세그먼트 트리
- 컴퓨터융합학부
- BOJ 30029
- 2023 Engineering Pair
- 알고리즘
- 충남대학교 2023 SW - IT
- Dynamic Programming
- BOJ 30027
- BOJ 30026
- dx dy
- Delaunay triangulation
- BOJ 30028
- Problem Solving
- CodeForces
- BOJ17139
- 오일러투어트리
- 백준
- BOJ 31226
- boj 30788
- 2023 SW - IT Contest
- 세그먼트 트리
- fortune's algorithm
- 27173
- boj23054
- hhs2003
- Sakura Reflection
- voronoi diagram
- 수열과 쿼리 43
- 27114
- 누텔라트리(hard)
알고리즘 일지
BOJ 27577 - Everything is A Nail 본문
문제 링크
문제 풀이
오랜만에 BOJ를 탐닉하다... 사람들이 풀고 있길래... 찍먹을 해보게 되었습니다..
간단하게 설명하면, 처리해야 하는 작업의 종류가 0, 1, 2 세 가지가 있고, 각 작업이 순서대로 주어집니다.
이때, 작업을 처리해야 하는 도구가 존재하고, 첫 번째 도구는 마음대로... 그리고 다른 도구들로 한 번씩 변경 가능합니다.
즉, 배열안을 세 파티션으로 나눈 다음, 각각의 지정된 원소가 몇 개 있는지 총합한 값을 최대로 만드는 문제로 바라볼 수 있겠습니다.
근데 도구 선택이 자유로우므로, 도구를 드는 순서 3! 번 다 정하고 해보면 되겠습니다.
다르게 풀수도 있지만, 상위문제에서는 이런 유형의 문제가 많이 존재하는데요. 거의 작업종류가 엄청 많게 하고, 조금 더 응용이 들어가는 문제들이 많이 나옵니다.
상위 문제에서 자주 쓰는 dp 테크닉을 가져와 봅시다.
$dp[i][j]$를 $i$번째 도구로 $j$번째까지 일을 하고 도구를 교체하기 전의 일을 처리한 최대 값이라고 정의합시다.
나이브하게 식을 한번 세워봅시다.
$$dp[i][j] = \max_{k \; < \; j} \bigg(dp[i-1][k] + \sum_{g=k+1}^{j} \big(\; 1 \;\;\; \text{if g'th task is i}\big)\bigg)$$
이러면 일단 $O(N^3)$ 입니다. 최적화를 시작해 봅시다. 일단 안쪽의 $k+1$번째부터 $j$번째 존재하는 $i$종류의 작업의 개수는 Prefix Sum (누적합)을 이용하여, 쉽게 구할 수 있습니다. 고쳐봅시다.
$\text{prefix}[i][j]$ 를 $j$번째까지 존재하는 종류가 $i$인 작업의 갯수라고 정의합시다.
$$dp[i][j] = \max_{k \; < \; j} \bigg(dp[i-1][k] + \text{prefix}[i][j] - \text{prefix}[i][k] \bigg)$$
이렇게 하면, 구간에 존재하는 종류가 $i$인 작업의 갯수를 $O(1)$에 구할 수 있습니다. 하지만 여전히 시간복잡도는 $O(N^2)$입니다. 어떻게 해결해야 할까요?
dp점화식에서 상수로 작용하는 부분을 한번 빼볼까요!
$$dp[i][j] = \max_{k \; < \; j} \bigg(dp[i-1][k] - \text{prefix}[i][k] \bigg) + \text{prefix}[i][j]$$
$\max$ 함수 안쪽에 있는 $ dp[i-1][k] - \text{prefix}[i][k]$ 을 하나의 배열로 다시 만들어 봅시다.
$$dp[i][j] = \max_{k \; < \; j} \big( A[k] \big)+ \text{prefix}[i][j]$$
$j$에 대해서 왼쪽에 있는 $A[k]$는 선형시간에 한 번에 밀면서 구할 수 있습니다. 이런 식으로 최적화를 걸면 $O(N)$에 점화식을 구할 수 있습니다.
여기서 중요한 테크닉은 prefixSum에서 상수를 구분하고 뺀 다음, 상수가 아닌 부분은 배열처리를 새롭게 하는 부분입니다. 꼭 기억해 두세요~
#include<bits/stdc++.h>
using namespace std;
int prefix[3][303030], N;
vector<int> ord;
int solve () {
vector<int> A(303030), B(303030);
for (int i=0;i<N;i++) {
A[i] = prefix[ord[0]][i] - prefix[ord[1]][i];
}
for (int j=1;j<=2;j++) {
int mx = 0;
for (int i=0;i<N;i++) {
B[i] = prefix[ord[j]][i] + mx;
mx = max(mx, A[i]);
}
swap(A, B);
std::fill(B.begin(), B.end(), 0);
if (j == 2) break;
for (int i=0;i<N;i++) {
A[i] -= prefix[ord[j+1]][i];
}
}
return A[N-1];
}
int ans = 0;
void rec (int depth) {
if (depth == 3) {
ans = max(solve(), ans);
return;
}
for (int i=depth;i<3;i++) {
swap(ord[i], ord[depth]);
rec(depth+1);
swap(ord[i], ord[depth]);
}
}
int main () {
cin >> N;
ord.push_back(0);
ord.push_back(1);
ord.push_back(2);
for (int i=0;i<N;i++) {
int g; cin >> g;
prefix[g][i]++;
}
for (int i=0;i<N;i++) {
for (int j : {0, 1, 2}) {
prefix[j][i+1] += prefix[j][i];
}
}
rec(0);
cout << ans;
}
'알고리즘 풀이' 카테고리의 다른 글
Fortune's Algorithm 에 대하여 (11) | 2024.11.15 |
---|---|
BOJ 30788 - Sakura Reflection (0) | 2024.10.28 |
2024 SW - IT Contest 문제 풀이 (1) | 2024.09.29 |
BOJ 1146 - 지그재그 서기 (0) | 2024.08.06 |
BOJ 9616 - 홀수 정사각형 (0) | 2024.08.03 |