Basic
Dynamic Programming
日月卦長
Prerequisite
- High Dimensional Array
- Recursion
- Bitwise operator
- Math
Fibonacci number
\(F_n = F_{n-1}+F_{n-2}\)
\(F_0=0\\F_1=1\)
Loop
Recursion
long long F[100+5];
// ...
F[0] = 0;
F[1] = 1;
for(int i=2; i<=100; ++i){
F[i] = F[i-1] + F[i-2];
}
// ...
cout << F[100] << '\n';long long F(int i){
if(i == 0) return 0;
if(i == 1) return 1;
return F(i-1) + F(i-2);
}
// ...
cout << F(100) << '\n';Recursion Test
#include <bits/stdc++.h>
using namespace std;
long long F(int i){
if(i==0 || i==1) return i;
return F(i-1) + F(i-2);
}
int main(){
for(int i = 0; i < 50; ++i){
cout << i << ": " << F(i) << '\n';
}
return 0;
}Recursion Test 2
#include <bits/stdc++.h>
using namespace std;
long long DP[1000];
long long F(int i){
if(i==0 || i==1) return i;
if(DP[i]!=0) return DP[i];
return DP[i] = F(i-1) + F(i-2);
}
int main(){
for(int i = 0; i < 50; ++i){
cout << i << ": " << F(i) << '\n';
}
return 0;
}Recursion Test 2
#include <bits/stdc++.h>
using namespace std;
long long DP[1000];
long long F(int i){
if(i==0 || i==1) return i;
if(DP[i]!=0) return DP[i];
return DP[i] = F(i-1) + F(i-2);
}
int main(){
for(int i = 0; i < 50; ++i){
cout << i << ": " << F(i) << '\n';
}
return 0;
}sufficient and necessary condition
of
Dynamic Programming
- Overlapping subproblems
- Optimal substructure
Some conditions and transition formula
Overlapping subproblems
$$F_n$$
$$F_{n-1}$$
$$F_{n-2}$$
$$F_{n-2}$$
$$F_{n-3}$$
- Can be broken down into many subproblems
- We call every subproblem:state
Optimal substructure
- The Answer can be constructed from the answer of subproblems
- Write them into the formula
- State Transition
\(F_0=0\\F_1=1\)
\(F_n = F_{n-1}+F_{n-2}\)
Time Complexity
- Num of states * Transition Time
\(F_0=0\\F_1=1\)
\(F_n = F_{n-1}+F_{n-2}\)
- Num of states: \(n\)
- Transition: \(O(1)\)
- Total
- : \(n\times O(1)=O(n)\)
Bottom Up
Top Down
long long F[100+5];
// ...
F[0] = 0;
F[1] = 1;
for(int i=2; i<=100; ++i){
F[i] = F[i-1] + F[i-2];
}
// ...
cout << F[100] << '\n';long long DP[100+5];
long long F(int i){
if(i == 0) return 0;
if(i == 1) return 1;
if(DP[i]!=0) return DP[i];
return DP[i] = F(i-1) + F(i-2);
}
// ...
cout << F(100) << '\n';Coding
Bottom Up
- Only loops
- Run fast
- Clear Transition
- Can do more Optimization
Top Down
- Recursion
- Run slow
- Easier to think
Thinking in DP
- Feel like DP
- State Definition
- State Transition based on state definition
- Check Time Complexity
- Coding
Knapsack Problem
Classic
Knapsack Problem
- A backpack can hold weight \(T\)
- There're \(n\) items with weight\(c_1,c_2,\dots,c_n\) respectively
- Their values are \(w_1,w_2,\dots,w_n\)
- How much value we can take at most?
State Definition
- \(d(i,v)\) represent:
- items from \(1 \sim i\)
- Pick some of them into a backpack. It can hold at most weight \(v\).
- The largest value it can be
State Transition
- \(d(i,v)=0\) if \(i=0\) or \(v = 0\)
- \(d(i,v)=-\infty\) if \(v<0\)
-
\(d(i,v)=max(d(i-1,v), d(i-1,v-c_i)+w_i)\)
if \(i>1\) and \(v > 0\)
- \(d(n,T)\) is the answer!
Time Complexity
- Num of states: \(n\times T\)
- Transition Time: \(O(1)\)
- Total: \(n\times T\times O(1)=O(n\times T)\)
Top Down
int dp[MAXN][MAXT] = {};
bool vis[MAXN][MAXT] = {};
int d(int i, int v){
if(i==0||v==0) return 0;
if(v<0) return -9999999;
if(vis[i][v]) return dp[i][v];
vis[i][v] = true;
return dp[i][v] =
max(d(i-1,v), d(i-1,v-c[i])+w[i]);
}Bottom Up
int dp[MAXN][MAXT] = {};
for(int i=1; i<=n; ++i)
for(int v=c[i]; v<=T; ++v)
dp[i][v] =
max(dp[i-1][v], dp[i-1][v-c[i]]+w[i]);Reuse Array
int dp[2][MAXT] = {};
for(int i=1; i<=n; ++i)
for(int v=c[i]; v<=T; ++v)
dp[i&1][v] =
max(dp[(i-1)&1][v], dp[(i-1)&1][v-c[i]]+w[i]);
int ans = dp[n&1][T];State Compression
int dp[MAXT] = {};
for(int i=1; i<=n; ++i)
for(int v=T; v>=c[i]; --v)
dp[v] = max(dp[v], dp[v-c[i]]+w[i]);
int ans = dp[T];Reference
Practices
BasicDynamicProgramming from SunMoon
By tunchin kao
BasicDynamicProgramming from SunMoon
- 77