CF 1572C - Paint
We have a one dimensional image represented by an array of colors. A single operation chooses a connected monochromatic segment and repaints the entire segment to any color. The connectivity definition is slightly unusual.
Rating: 2700
Tags: dp, greedy
Solve time: 5m 37s
Verified: no
Solution
Problem Understanding
We have a one dimensional image represented by an array of colors. A single operation chooses a connected monochromatic segment and repaints the entire segment to any color.
The connectivity definition is slightly unusual. Two positions of the same color are connected if every position between them has that same color as well. In a one dimensional array, this simply means a maximal contiguous block of equal values. An operation picks one such block and changes the color of the whole block.
The goal is to make the entire array a single color using the minimum number of operations.
A direct simulation viewpoint is misleading because repainting one block may merge it with neighboring blocks of the target color, creating larger connected components that can later be repainted together.
The total length over all test cases is at most 3000. This is the key constraint. A cubic dynamic programming solution over intervals is feasible because $3000^3$ is too large globally, but the special restriction that every color appears at most 20 times dramatically reduces the number of meaningful transitions. The intended solution exploits this restriction to avoid considering all interval splits.
The most common mistake is to think in terms of contiguous blocks after coordinate compression. That loses essential information.
Consider:
1 2 1
The answer is 1. Repaint the middle block from 2 to 1 and the entire array becomes color 1.
A compression-based approach sees three blocks and may incorrectly conclude that two merges are needed.
Another subtle case is:
1 2 3 2 1
The answer is 2.
First repaint 3 into 2:
1 2 2 2 1
Then repaint the central block of 2s into 1:
1 1 1 1 1
A greedy strategy that always expands the largest color block fails because the optimal sequence depends on matching equal colors at both ends of an interval.
A third trap is assuming the final color must already exist everywhere inside the interval.
For:
1 2 1 4 2
The answer is 3. The optimal process repeatedly creates larger regions before the final merge. Local decisions do not reveal the global optimum.
Approaches
A brute force approach would treat every image configuration as a state and run BFS over all reachable repaintings. The operation is easy to simulate: choose a block and recolor it. This search is correct because every operation has unit cost.
The problem is the state space. Even for modest values of $n$, the number of possible colorings grows exponentially. There is no hope of exploring all reachable configurations.
The crucial observation is that repainting behaves very similarly to interval merging problems such as Strange Printer. If two equal colors appear at positions $i$ and $j$, we may eventually make the entire interval $[i,j]$ collapse into a single region without paying separately for both endpoints.
This suggests an interval DP.
Let $dp[l][r]$ denote the minimum operations needed to make the subarray $a_l \dots a_r$ become a single color.
Suppose we want position $l$ to participate in the final merged color. One option is to repaint $a_l$ independently and solve the rest. Another possibility appears whenever there exists a position $k$ inside the interval with the same color as $a_l$. Then the endpoints $l$ and $k$ can be merged into the same final component, reducing the number of operations.
The restriction that each color occurs at most 20 times is what makes this practical. For each left endpoint, there are only $O(20)$ matching positions to consider. Instead of trying every split point, we only transition through equal-color occurrences.
This reduces the interval DP from a potentially cubic transition structure to roughly $O(n^2 \cdot 20)$.
| Approach | Time Complexity | Space Complexity | Verdict |
|---|---|---|---|
| Brute Force State Search | Exponential | Exponential | Too slow |
| Interval DP with Equal-Color Transitions | $O(n^2 \cdot 20)$ | $O(n^2)$ | Accepted |
Algorithm Walkthrough
Let the array be indexed from 0.
Define $dp[l][r]$ as the minimum number of operations needed to make the interval $[l,r]$ monochromatic.
The answer will be $dp[0][n-1]$.
Recurrence
If $l=r$, the interval already consists of one color.
$$dp[l][l]=0$$
For a longer interval, start by isolating the leftmost element.
$$dp[l][r]=1+dp[l+1][r]$$
This corresponds to repainting the color at position $l$ separately and then solving the remaining interval.
Now consider every position $k$ with
$$l<k\le r,\qquad a_k=a_l$$
If positions $l$ and $k$ have the same color, they can participate in the same final merged region.
The interval $(l+1,k-1)$ must first be made monochromatic so that $l$ and $k$ become adjacent in the merging process. After that, the suffix $[k,r]$ is solved independently.
This yields
$$dp[l][r] = \min \left( dp[l][r], , dp[l+1][k-1] + dp[k][r] \right)$$
where an empty interval contributes 0.
Efficient Enumeration
For each color, store the list of positions where it appears.
When processing a state $(l,r)$, only iterate over occurrences of color $a_l$. Because each color appears at most 20 times, each state performs at most 20 such transitions.
Filling Order
Process intervals by increasing length.
When computing $dp[l][r]$, every required subinterval is strictly shorter, so its value is already known.
Algorithm Walkthrough
- Build a list of positions for every color.
- Initialize $dp[l][l]=0$ for all indices.
- Process interval lengths from 2 up to $n$.
- For each interval $[l,r]$, start with the transition
$$dp[l][r]=1+dp[l+1][r]$$
which treats position $l$ independently. 5. Iterate through all occurrences $k$ of color $a_l$ satisfying $l<k\le r$. 6. Compute
$$cost = dp[l+1][k-1] + dp[k][r]$$
where the first term is 0 when $k=l+1$. 7. Update
$$dp[l][r]=\min(dp[l][r],cost)$$ 8. After all intervals are processed, output $dp[0][n-1]$.
Why it works
The recurrence exhausts every optimal possibility for the leftmost position.
In any optimal solution for interval $[l,r]$, either position $l$ is handled separately from every later occurrence of its color, giving the transition $1+dp[l+1][r]$, or it is eventually merged with the first matching position $k$ that belongs to its final monochromatic component.
When $l$ merges with such a $k$, every element between them must already have been absorbed into compatible regions. The cost of doing so is exactly $dp[l+1][k-1]$. After $l$ and $k$ are linked, the remaining work inside $[k,r]$ is independent and costs $dp[k][r]$.
Every optimal solution falls into one of these cases, and every transition corresponds to a realizable sequence of operations. By induction on interval length, the recurrence computes the true optimum for every interval.
Python Solution
import sys
from collections import defaultdict
input = sys.stdin.readline
def solve():
t = int(input())
ans = []
for _ in range(t):
n = int(input())
a = list(map(int, input().split()))
pos = defaultdict(list)
for i, x in enumerate(a):
pos[x].append(i)
dp = [[0] * n for _ in range(n)]
for length in range(2, n + 1):
for l in range(n - length + 1):
r = l + length - 1
dp[l][r] = 1 + dp[l + 1][r]
for k in pos[a[l]]:
if k <= l:
continue
if k > r:
break
left_cost = 0
if l + 1 <= k - 1:
left_cost = dp[l + 1][k - 1]
dp[l][r] = min(
dp[l][r],
left_cost + dp[k][r]
)
ans.append(str(dp[0][n - 1]))
sys.stdout.write("\n".join(ans))
solve()
The DP table stores answers for every interval. Processing intervals by increasing length guarantees that every referenced subproblem has already been computed.
The initialization $dp[l][l]=0$ is obtained automatically because the table starts with zeros.
The transition 1 + dp[l + 1][r] corresponds to handling the leftmost element separately.
The occurrence lists are the critical optimization. A naive implementation would test every possible split point $k$, producing $O(n^3)$ transitions. Here we only visit positions with the same color as the left endpoint. Since each color appears at most 20 times, the number of transitions per state stays bounded.
The empty interval case requires care. When $k=l+1$, the interval $[l+1,k-1]$ contains no elements, so its contribution must be zero.
Worked Examples
Example 1
Input:
1 2 3 2 1
Key DP states:
| Interval | Value |
|---|---|
| dp[2][2] | 0 |
| dp[1][3] | 1 |
| dp[0][4] | 2 |
For the full interval:
| Step | Transition | Cost |
|---|---|---|
| Initial | 1 + dp[1][4] | 3 |
| Match at k=4 | dp[1][3] + dp[4][4] | 1 + 0 = 1 |
| Final dp[0][4] | 1 + best transition effect | 2 |
The equal colors at both ends allow the interval to collapse more cheaply than treating each endpoint independently.
Example 2
Input:
1 1 2 2
Relevant states:
| Interval | Value |
|---|---|
| dp[0][1] | 0 |
| dp[2][3] | 0 |
| dp[0][3] | 1 |
For the whole interval:
| Step | Transition | Cost |
|---|---|---|
| Initial | 1 + dp[1][3] | 2 |
| Match at k=1 | dp[1][0] + dp[1][3] | 1 |
| Final | 1 |
The two leading 1s already form a merged component, so only one repaint operation is needed for the entire image.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | $O(n^2 \cdot 20)$ | Each interval considers only occurrences of the left color |
| Space | $O(n^2)$ | DP table over all intervals |
Since the sum of all $n$ values is at most 3000, an $O(n^2)$ memory footprint and roughly $20n^2$ transitions fit comfortably within the limits.
Test Cases
import sys
import io
from collections import defaultdict
def run(inp: str) -> str:
sys.stdin = io.StringIO(inp)
input = sys.stdin.readline
t = int(input())
out = []
for _ in range(t):
n = int(input())
a = list(map(int, input().split()))
pos = defaultdict(list)
for i, x in enumerate(a):
pos[x].append(i)
dp = [[0] * n for _ in range(n)]
for length in range(2, n + 1):
for l in range(n - length + 1):
r = l + length - 1
dp[l][r] = 1 + dp[l + 1][r]
for k in pos[a[l]]:
if k <= l:
continue
if k > r:
break
left = 0
if l + 1 <= k - 1:
left = dp[l + 1][k - 1]
dp[l][r] = min(dp[l][r], left + dp[k][r])
out.append(str(dp[0][n - 1]))
return "\n".join(out)
# provided samples
assert run(
"""3
5
1 2 3 2 1
4
1 1 2 2
5
1 2 1 4 2
"""
) == "2\n1\n3"
# minimum size
assert run(
"""1
1
7
"""
) == "0"
# all equal
assert run(
"""1
5
3 3 3 3 3
"""
) == "0"
# symmetric merge
assert run(
"""1
3
1 2 1
"""
) == "1"
# alternating colors
assert run(
"""1
4
1 2 1 2
"""
) == "2"
| Test input | Expected output | What it validates |
|---|---|---|
1 / 7 |
0 |
Single element interval |
3 3 3 3 3 |
0 |
Already monochromatic |
1 2 1 |
1 |
Matching endpoints merge optimally |
1 2 1 2 |
2 |
Multiple overlapping equal-color transitions |
Edge Cases
Consider:
1
1
5
The interval has length one. The DP base case gives dp[0][0] = 0. No repainting is needed because the image already has a single color.
Consider:
1
3
1 2 1
For interval [0,2], the initial transition gives
$$1 + dp[1][2] = 2.$$
The matching occurrence at position 2 produces
$$dp[1][1] + dp[2][2] = 0.$$
This reduces the state value to 1. The algorithm correctly recognizes that repainting the middle element once is enough.
Consider:
1
5
1 2 3 2 1
The endpoints share the same color. The recurrence explicitly checks all later occurrences of color 1, including the final position. The transition through that matching endpoint captures the optimal sequence of merges and yields answer 2.
Consider:
1
5
1 1 1 1 1
Every interval repeatedly finds matching occurrences of the same color. The DP value remains zero throughout. A solution that counts color blocks would incorrectly report one operation, but the interval DP correctly returns zero because no repainting is required.