Appearance
Convex Hull Optimization
Manufacturing DP
Prefix sums turn every split point into a line candidate, while the deque keeps only the useful hull.
O(n)dequelower hull
01Prefixs(i)
02Querybest j
03Insertnew point
04Answerf[n]
Minimize Manufacturing Cost
Input: a sequence of items with cost
Operation:
man(l, r): manufacture items fromto in one batch. - Batch cost:
Goal: manufacture all items with minimum total cost.
Basic DP
Let
and let
If the last batch is
So:
The direct DP enumerates every
Why this case needs optimization
The slides compare three cost functions:
, namely
Only the first one is interesting.
- If the cost is
, the sum of all is fixed, so when we simply use one batch. - If
and all , splitting is always no worse because . - When both
and the square term exist, there is a trade-off: fewer batches save fixed cost , but larger batches increase the square term.
Math Time
For fixed
Cancel
Move terms:
Define:
Then:
Intuition: view each split point
Then
Convex Hull Maintenance
Maintain a deque of candidate split points:
The deque keeps useful candidates only.

Query
Since
While the second candidate is already better than the first:
pop
After this process,
Insert
After computing
Suppose the current last two candidates are
then
Repeat this until the slopes are increasing, then push
Algorithm
text
s[0] = 0
for i = 1 to n:
s[i] = s[i - 1] + a[i]
f[0] = 0
deque = [0]
for i = 1 to n:
while deque has at least 2 elements
and g(deque[0], deque[1]) <= s[i]:
pop front
j = deque[0]
f[i] = f[j] + C + (s[i] - s[j])^2
while deque has at least 2 elements
and g(deque[-2], deque[-1]) >= g(deque[-1], i):
pop back
push back i
answer = f[n]Running Time
Each index is:
- inserted once,
- popped from the front at most once,
- popped from the back at most once.
So the total amortized time is:
The space complexity is:
Bellman-Ford as DP
The DP guideline for graph problems is still the same:
- Define subproblems.
- Check the DAG structure and find a topological order.
- Solve and store subproblems in that order.
Bellman-Ford can be understood as a DP on the number of edges used in the path.
Let
be the shortest distance from source
Initialization:
Transition:
Interpretation:
- Case 1: the best path to
uses at most edges. - Case 2: the best path to
uses one last edge after a path from to using at most edges.
After
If the graph has no negative cycle, every shortest path can be chosen as a simple path, so it uses at most
is the final shortest distance.
If one more round still improves some distance:
then there is a negative cycle reachable from the source.
The running time is:
With rolling arrays, the space can be reduced to:
Extending Bellman-Ford to All-Pairs Shortest Path
All-pairs shortest path asks for:
for every ordered pair of vertices
A naive plan is to run Bellman-Ford once from every source:
Natural Generalization
A direct extension of the Bellman-Ford state is:
meaning the shortest distance from
Transition:
This is correct, but it does not really improve the algorithm.
Reason:
- For each fixed start vertex
, the recurrence is just Bellman-Ford from . - Different start vertices are independent.
- In each round, every edge can update distances for every possible start vertex.
So the time is still:
This is the same as running Bellman-Ford
The problem is not the recurrence itself. The problem is that the state definition does not create useful interaction between different starting vertices.
Floyd-Warshall: A Better DP State
Floyd-Warshall changes the meaning of the DP dimension.
Label the vertices:
Define:
as the shortest distance from
Here, intermediate vertices exclude the two endpoints
Initialization:
When
Transition
To compute
Case 1: it does not use
Case 2: it uses
If there is no negative cycle, a shortest path does not need to visit
and
Both subpaths only use intermediate vertices from
So:
Topological Order
The state
Therefore the topological order is:
text
for k = 1 to n:
for each u in V:
for each v in V:
compute dist[k, u, v]The running time is:
If we store every layer, the space is:
Simpler In-Place Implementation
We can keep only one matrix:
text
function floyd_warshall(G):
for u in V:
for v in V:
if u == v:
dist[u][v] = 0
else if (u, v) in E:
dist[u][v] = w(u, v)
else:
dist[u][v] = infinity
for k in V:
for u in V:
for v in V:
dist[u][v] = min(dist[u][v], dist[u][k] + dist[k][v])The time is still:
but the space becomes:
The in-place version is correct because, during phase
Traveling Salesman Problem
Input: a complete weighted undirected graph
Goal: find a minimum-weight cycle that visits every vertex exactly once.
This is the Traveling Salesman Problem, or TSP.
旅行商问题是NP-HARD 问题,Brute force enumerates all possible visiting orders, so the running time is:
where
Why Floyd-Warshall's state is not enough
For shortest path, Floyd-Warshall uses:
meaning the shortest path from
For TSP, a tempting idea is:
meaning the shortest path from
except endpoints
Then the answer seems to be:
But this state is not enough.
If we try to split the path through some vertex
and
must use disjoint sets of intermediate vertices. The state
So we need to remember the exact set of visited vertices.
Plan B: Set DP
Define:
as the shortest path from
To solve it, choose the last intermediate vertex
Then:
The topological order is by the size of
text
for size = 0 to n:
for each set S with |S| = size:
compute states using smaller sets S - {x}This DP is correct, but it has too many endpoint choices. We can simplify it by fixing the starting vertex.
Fix one start vertex
In an undirected TSP cycle, the starting point is arbitrary. So fix one vertex
Define:
as the minimum cost of a path that:
- starts from
, - visits exactly the vertices in
, - ends at
.
Here
Base case:
Transition:
The final answer closes the cycle by returning to
Bitmask Implementation
The set
text
function tsp(G):
choose start vertex r = 0
dp[mask][v] = infinity
dp[1 << r][r] = 0
for mask = 0 to (1 << n) - 1:
if r not in mask:
continue
for v = 0 to n - 1:
if v not in mask:
continue
for x = 0 to n - 1:
if x in mask and x != v:
dp[mask][v] =
min(dp[mask][v],
dp[mask without v][x] + w(x, v))
answer = infinity
full = (1 << n) - 1
for v = 0 to n - 1:
if v != r:
answer = min(answer, dp[full][v] + w(v, r))
return answerComplexity
There are:
states, because there are
Each state enumerates the previous endpoint
So the total running time is:
The space complexity is:
This is still exponential, but it is much better than brute force:
The improvement comes from merging repeated subproblems. Many different visiting orders can have the same visited set