14 dp4
14 dp4
on Trees
Longest Increasing Subsequence
0 1 2 3 4 5 6 7
5 -6 3 6 -5 2 8 10
Is it allowed?
Will the sequence still be increasing if it’s included?
5 -6 3 6 -5 2 8 10
Recursive call is best value in this area Current 𝑖 Not yet processed.
𝑗
0, 5 1, −6 2, 3 3, 6 4, −5 5, 2 6, 8 7, 10
0, 5 1 0 0 1 0 0 1 1
1, −6 1
2, 3
3, 6
𝑖
4, −5
5, 2
6, 8
7, 10
𝐿𝐼𝑆 1,1 𝐴 1 ≥ 𝐴[1] can add, 1 + 𝐿𝐼𝑆(0,1) or 𝐿𝐼𝑆(0,1)
LIS
𝑗
0, 5 1, −6 2, 3 3, 6 4, −5 5, 2 6, 8 7, 10
0, 5 1 0 0 1 0 0 1 1
1, −6 1 1
2, 3
3, 6
𝑖
4, −5
5, 2
6, 8
7, 10
𝐿𝐼𝑆 1,2 𝐴 1 ≤ 𝐴[2] allowed to add:
LIS 1 + 𝐿𝐼𝑆 0,1 or 𝐿𝐼𝑆(0,2)
𝑗
0, 5 1, −6 2, 3 3, 6 4, −5 5, 2 6, 8 7, 10
0, 5 1 0 0 1 0 0 1 1
1, −6 1 1 1
2, 3
3, 6
𝑖
4, −5
5, 2
6, 8
7, 10
𝐿𝐼𝑆 2,0 𝐴 2 ≤ 𝐴 0 allowed to add
LIS 1 + 𝐿𝐼𝑆(1,2) or 𝐿𝐼𝑆(1,0)
𝑗
0, 5 1, −6 2, 3 3, 6 4, −5 5, 2 6, 8 7, 10
0, 5 1 0 0 1 0 0 1 1
1, −6 1 1 1 1 1 1 1 1
2, 3 2
3, 6
𝑖
4, −5
5, 2
6, 8
7, 10
LIS
𝑗
0, 5 1, −6 2, 3 3, 6 4, −5 5, 2 6, 8 7, 10
0, 5 1 0 0 1 0 0 1 1
1, −6 1 1 1 1 1 1 1 1
2, 3 2 1 2 2 1 1 2 2
𝑖 3, 6 2 1 2 3 1 1 3 3
4, −5 2 1 2 3 2 2 3 3
5, 2 3 1 3 3 2 3 3 3
6, 8 3 1 3 3 2 3 4 4
7, 10 3 1 3 3 2 3 4 5
pseudocode
//real code snippet that actually generated the table on the last slide
for(int j=0; j < n; j++){
vals[0][j] = (A[0] <= A[j]) ? 1 : 0;
}
for(int i = 1; i < 8; i++){
for(int j = 0; j < n; j++){
if(A[i] > A[j])
vals[i][j] = vals[i-1][j];
else{
vals[i][j] = Math.max(1+vals[i-1][i], vals[i-1][j]);
}
}
}
Longest Increasing Subsequence
0 if 𝑖 < 0
𝕀[𝐴 𝑖 ≤ 𝐴 𝑗 ] if 𝑖 = 0
𝐿𝐼𝑆 𝑖, 𝑗 =
𝐿𝐼𝑆 𝑖 − 1, 𝑗 if 𝐴 𝑖 > 𝐴 𝑗
max 1 + 𝐿𝐼𝑆 𝑖 − 1, 𝑖 , 𝐿𝐼𝑆 𝑖 − 1, 𝑗 otherwise
5 -6 3 6 -5 2 8 10
Not yet processed. Current 𝑖 Recursive call is best value in this area
5 -6 3 6 -5 2 8 10
Not yet processed. Current 𝑖 Recursive call is best value in this area
0 if 𝑖 > 𝑛 𝑜𝑟 𝑗 > 𝑛
𝐿𝐼𝑆𝐴𝑙𝑡 𝑖, 𝑗 = ቐ𝐿𝐼𝑆 𝑖 + 1, 𝑗 if 𝐴 𝑖 > 𝐴[𝑗]
max{1 + 𝐿𝐼𝑆 𝑖 + 1, 𝑖 , 𝐿𝐼𝑆 𝑖 + 1, 𝑗 } o/w
Longest Increasing Subsequence
𝐿𝐼𝑆𝐴𝑙𝑡 𝑖, 𝑗 is “Number of elements of the maximum increasing
subsequence from 𝑖, … , 𝑛 where smallest element of the sequence is 𝐴[𝑗]”
0 if 𝑖 > 𝑛 𝑜𝑟 𝑗 > 𝑛
𝐿𝐼𝑆𝐴𝑙𝑡 𝑖, 𝑗 = ቐ 𝐿𝐼𝑆𝐴𝑙𝑡 𝑖 + 1, 𝑗 if 𝐴 𝑖 > 𝐴[𝑗]
max{1 + 𝐿𝐼𝑆𝐴𝑙𝑡 𝑖 + 1, 𝑖 , 𝐿𝐼𝑆𝐴𝑙𝑡 𝑖 + 1, 𝑗 } o/w
Memoization structure? 𝑛 × 𝑛 array.
Filling order? Multiple possible
Outer loop: 𝑖 from 0 to 𝑛 − 1
Inner loop: 𝑗 from 𝑛 − 1 to 𝑖
Summing Up
The two recurrences have the same idea (add/don’t add, record the end
of the array closest to your next decision)
But thinking left-to-right vs. right-to-left
Instead of thinking “do I include this element or not?” for each element,
Ask “what’s the next element” or equivalently “what’s the longest
subsequence starting from me”
Get a different recurrence, but not a better running time.
Takeaways
When designing a dynamic program, we sometimes need to introduce
a second variable, that doesn’t appear in the program
Or a second recurrence that mixes with the first if other decisions affect
what’s optimal (beyond which problem you look at)
0 1 2 3 4 5 6 7
5 6 3 6 5 2 8 10
Write a recurrence
Give a sentence or two (in English) of why your recurrence should work.
Subset Sum
Write an English description of what you want to calculate
Let 𝑆𝑈𝐵𝑆𝑈𝑀(𝑖, 𝑡) be true if and only if a subset of 𝐴 0 , … , 𝐴[𝑖] can sum to 𝑡.
Write a recurrence
𝑇𝑟𝑢𝑒 if 𝑡 = 0
𝑆𝑈𝐵𝑆𝑈𝑀 𝑖, 𝑡 = ቐ 𝐹𝑎𝑙𝑠𝑒 if 𝑖 < 0 and 𝑡 ≠ 0
𝑆𝑈𝐵𝑆𝑈𝑀 𝑖 − 1, 𝑡 ||𝑆𝑈𝐵𝑆𝑈𝑀 𝑖 − 1, 𝑡 − 𝐴[𝑖] 𝑜/𝑤
Give a sentence or two (in English) of why your recurrence should work.
Element 𝑖 is either included or it isn’t – if 𝑖 appears in a valid subset, then we need
to have the remaining elements sum to 𝑡 − 𝐴[𝑖]. If 𝑖 doesn’t appear then the
remaining elements will get to 𝑡. We “or” together because either could be a valid
path to getting the right sum.
Subset Sum
What memorization structure will you use?
A 2D Boolean array SUBSUM(𝑖, 𝑗). Array will be 𝑛 × 𝑇
Write the pseudocode to fill up the structure iteratively.
SubSum(int[] A, int T)
Bool[][] SubSum = new Bool[n][T+1]
for(int j=0;j<T+1;j++){ SubSum[0][j]=False;}
SubSum[0][A[0]]=True;
for(int i=1; i<n;i++){
for(int j=0; j<T+1; j++){
if(SubSum[i-1][j]){
SubSum[i][j]=True;
SubSum[i][j+A[i]]=True;//need to catch Array index errors. Don’t do
//this in real code.
}
}
}
return SubSum[n][T-1];
Longest Increasing Subsequence, Round 3
Let’s ask “what’s the best choice for the next element” (instead of just “is
this the next element”
What’s the best choice?
It has to be greater than our current element, after that it’s the one that
can lead to the longest subsequence.
So, (since we’re starting with our current element), the question is
“what’s the longest increasing subsequence, starting at index 𝑖”
Longest Increasing Subsequence, Round 3
Let 𝐿𝐼𝑆𝑆𝑡𝑎𝑟𝑡(𝑖) be the length of the longest increasing subsequence
among indices 𝑖 … 𝑛, that starts at index 𝑖.
Be careful!
Final answer is not 𝐿𝐼𝑆𝑆𝑡𝑎𝑟𝑡(𝑖).
It’s the maximum entry among 𝐿𝐼𝑆𝑆𝑡𝑎𝑟𝑡() array
DP on Trees
DP on Trees
Trees are recursive structures
The weight of a vertex cover is just the sum of the weights of the
vertices in the set.
We want to find the minimum weight vertex cover.
Vertex Cover
Vertex Cover A set 𝑆 of vertices is a vertex cover if for every
edge (𝑢, 𝑣): 𝑢 is in 𝑆, or 𝑣 is in 𝑆, (or both)
Find the minimum vertex cover in
a tree. 1
5 3 20
Vertex Cover
Vertex Cover A set 𝑆 of vertices is a vertex cover if for every
edge (𝑢, 𝑣): 𝑢 is in 𝑆, or 𝑣 is in 𝑆, (or both)
Find the minimum vertex cover in
a tree. 1
5 3 20
Vertex Cover
Vertex Cover A set 𝑆 of vertices is a vertex cover if for every
edge (𝑢, 𝑣): 𝑢 is in 𝑆, or 𝑣 is in 𝑆, (or both)
Find the minimum vertex cover in
a tree. 1
5 3 20
Vertex Cover
Vertex Cover A set 𝑆 of vertices is a vertex cover if for every
edge (𝑢, 𝑣): 𝑢 is in 𝑆, or 𝑣 is in 𝑆, (or both)
Notice, the minimum weight
vertex cover might have both 1
endpoints of some edges
Even though only one of 1, 8 is
required on the edge between
8
them, they are both required for 10
other edges.
20
Also an indication that greedy 5 3
probably won’t work!
Vertex Cover – Recursively
Let’s try to write a recursive algorithm first.
𝑂𝑃𝑇 𝑣 = ቊmin{σ𝑢:𝑢 is a child of 𝑣 𝐼𝑁𝐶𝐿𝑈𝐷𝐸 𝑢 , 𝑤𝑒𝑖𝑔ℎ𝑡 𝑣 + σ𝑢:𝑢 is a child of 𝑣 𝑂𝑃𝑇(𝑢)} if 𝑣 is not a leaf
0 if 𝑣 is a leaf
𝐼𝑁𝐶𝐿𝑈𝐷𝐸 𝑣 = 𝑤𝑒𝑖𝑔ℎ𝑡 𝑣 + σ𝑢:𝑢 is a child of 𝑣 𝑂𝑃𝑇(𝑢)
Vertex Cover Dynamic Program
What memoization structure should we use?
10 8
5 3 20
Vertex Cover Dynamic Program
What memoization structure should we use?
the tree itself!
DP proofs are almost always just “the code does the recurrence”
But that just moves the correctness question – why is the recurrence
correct?
And the proof of the recurrence being correct is almost always “I
included all the cases”
I’d rather you focus on checking it than trying to explain it.
DP history
So…why is it called “dynamic programming?”
Linear Programming