APIO 15 P1 Bali Sculptures¶
Problem¶
Problem Link¶
https://www.acmicpc.net/problem/10846
https://oj.uz/problem/view/APIO15_sculpture
Summary¶
길이 \(N\)의 수열 \(Y\)가 주어질 때, 이 수열을 \(X\) \((A \leq X \leq B)\)개의 구간으로 쪼개어 각 구간별 \(Y_i\)의 합들의 bitwise OR을 최소화하여라.
Constraints¶
- \(1 \leq N \leq 2,000\)
- \(1 \leq A \leq B \leq N\)
- \(0 \leq Y_i \leq 10^9\)
Subtask 4 : ** \(1 \leq N \leq 100\), \(1 \leq A \leq B \leq N\)
Subtask 5 : ** \(1 \leq N \leq 2,000\), \(1 = A \leq B \leq N\)
Solution¶
Subtask 4와 Subtask 5가 서로를 포함하지 않는, 두 개의 문제로 구성되어 있음에 유의하자.
Subtask 4의 경우, \(1 \leq N \leq 100\)이고, Subtask 5의 경우 \(1 \leq N \leq 2,000\)인 대신 \(A=1\)로, 가능한 구간의 개수를 최소화 시켜야 한다.
"\(P[i]:=Y[i]\)의 누적합 배열" 으로 정의한다.
Subtask 4¶
- \(1 \leq N \leq 100\), \(1 \leq A \leq B \leq N\)
생각할 수 있는 가장 직관적인 풀이는 다음과 같다.
Wrong Solution
\(dp[i][k]:=\)\(1 \sim i\)까지의 수들을 \(k\)개의 구간으로 쪼갤 때 각 구간합들의 bitwise OR 값의 최솟값
\(dp[i][k]=min_{j<i} (dp[j][k-1] \ | \ (P[i]-P[j]))\)
위 정의와 점화식의 DP를 통해 \(O(N^3)\)에 답을 구한다.
하지만, 위 풀이는 올바른 답을 보장하지 않는다.
DP가 최적해를 보장하기 위해서는 전체 문제를 최적으로 풀기 위해서 부분문제 또한 최적으로 풀어야 한다는 최적 부분 구조가 성립해야 한다.
만약 작은 부분문제에서 가능한 답이 10(2)와 01(2)였고, 큰 전체문제로 가기 위해서는 이 답에 10(2)를 추가해야 된다고 가정하자.
그렇다면 전체문제에서의 값은 각각 10(2), 11(2)가 되며, 전체 문제에서의 최적해는 10(2)이다.
하지만 부분문제에서의 최적해는 01(2)로, 이 최적해로 전체문제를 풀면 11(2)로, 최적해를 얻을 수 없다.
따라서 문제의 구조는 최적 부분 구조가 성립하지 않음을 알 수 있다.
특히, 많은 경우에 bitwise 연산을 할 때 최적 부분 구조가 성립하지 않는다.
문제의 답을 최고 비트부터 하나씩 결정해가는 풀이를 생각하자.
최적해의 \(T+1\)이상의 비트를 다 결정했다고 가정하고 \(T\)번째 비트를 결정하기 위하여 다음의 DP를 생각하자.
Definition 1
\(T\)를 최상위 비트에서부터 감소시키며, 최적해의 \(T+1\)이상의 비트를 다 결정했다고 가정하고, \(T\)번째 비트가 \(0\)이 될 수 있는지 확인하자.
\(dp[i][k]:=\)\(1 \sim i\)까지의 수들을 \(k\)개의 구간으로 쪼갤 때 각 구간합들을 bitwise OR한 값들 중, \(T+1\)이상의 비트는 지금까지 구한 최적해와 똑같으며, \(T\)번째 비트는 \(0\)이 되도록 할 수 있는가? (True / False)
위와 같이 정의한 후, \(dp[j][k-1] (j < i)\) 에서 DP값을 받아올 때, \(P[i]-P[j]\) 구간합을 bitwise OR함으로 인해 지금까지 구한 최적해의 상위 비트에 문제가 발생하지 않는지 확인하고, \(T\)번째 비트가 0이 될 수 있는지를 확인하여 transition을 한다.
위 DP의 경우, 가능/불가능을 따지고 있기 때문에 최적 부분 구조에 대한 문제가 발생하지 않는다. 또한, 최상위 비트부터 아래쪽으로, 최적해의 비트를 결정해 나가고 있는 방식으로 문제를 해결하니, 최적해를 무조건 찾을 수 있다. \(T\)번째 비트가 \(0\)이 될 수 있는지 확인하기 위해서는 \(dp[N][A], dp[N][A+1], \cdots, dp[N][B]\) 중에 참인 것이 있는지 확인하면 된다.
\(\log X\)개의 비트 각각을 \(O(N^2)\)개의 상태 하나를 \(O(N)\)번의 transition으로 문제를 해결하니, 전체 \(O(N^3\log X)\)에 문제를 해결할 수 있다.
CheckPoint
Definition 1과 같이 DP를 정의하면 최고 비트부터 하나씩 최적해를 결정할 수 있고, \(O(N^3\log X)\)에 문제를 해결할 수 있다.
Complexity
Time Complexity : \(O(N^3\log X)\)
Subtask 5¶
- \(1 \leq N \leq 2,000\), \(1 = A \leq B \leq N\)
Subtask 5의 경우 가능한 구간의 개수를 최소화 시켜야 하는 대신 \(N\)의 크기가 늘어났다.
이제, \(dp[i][k]\)의 True / False DP 대신, \(k\)항을 DP값으로 변환하여 조건을 만족하기 위한 구간의 개수의 최솟값을 저장하자.
Definition 2
\(T\)를 최상위 비트에서부터 감소시키며, 최적해의 \(T+1\)이상의 비트를 다 결정했다고 가정하고, \(T\)번째 비트가 \(0\)이 될 수 있는지 확인하자.
\(dp[i]:=\)\(1 \sim i\)까지의 수들을 여러 구간으로 쪼갤 때 각 구간합들을 bitwise OR한 값들 중, \(T+1\)이상의 비트는 지금까지 구한 최적해와 똑같으며, \(T\)번째 비트는 \(0\)이 되도록 할 수 있는 구간의 최소 개수
transition은 Subtask 4의 경우와 거의 유사하다. 조건을 만족시키기 불가능하거나 필요한 구간의 개수가 \(B\)를 넘어가는 경우, \(dp[i]\)에 무한히 큰 값을 넣어 해결할 수 있다. \(T\)번째 비트가 \(0\)이 될 수 있는지 확인하기 위해서는 \(dp[N]\)이 \(B\)보다 작거나 같은지 확인하면 된다.
CheckPoint
구간의 개수를 최소화시키면 된다는 점을 이용하면 Definition 2와 같이 DP를 수정하면 최고 비트부터 하나씩 최적해를 결정할 수 있고, \(O(N^2\log X)\)에 문제를 해결할 수 있다.
Complexity
Time Complexity : \(O(N^2\log X)\)