# DP 一個由小問題推到大問題的演算算 ## 費氏數列 ### 純遞迴版 ```cpp= int fib(int N) { if( N == 1 || N == 0 ) return 1; return fib(N-1)+fib(N-2); } ``` ```graphviz digraph { a[label="4"] b[label="3"] c[label="2"] d[label="1"] e[label="0"] a -> b[color=red] b -> c[color=red] c -> d[color=red] c -> e[color=red] d -> c[label="1", color=green] e -> c[label="1", color=green] c -> b[label="2", color=green] f[label="1"] b->f[color=red] f->b[label="1", color=green] b->a[label="3", color=green] g[label="2"] a->g[color=red] h[label="1"] g->h[color=red] h->g[label="1", color=green] i[label="0"] g->i[color=red] i->g[label="1", color=green] g->a[label="2", color=green] } ``` $$時空複雜度O(\phi^N)$$ ### 動態規劃版 ```cpp= int save[mxN]; //mxN==100000 save[0] = save[1] = 1; int fib(int N) { if( save[N] ) return save[N]; //if( save[N] != 0 ) else { save[N] = fib(N-1)+fib(N-2); return save[N]; } } ``` ```graphviz digraph { "4" -> "3" "3" -> "2" "2" -> "1" "1" -> "2"[label=1, color=green] "2" -> "0" "0" -> "2"[label=1, color=green] "2" -> "3"[label=2, color=green] "3" -> "4"[label=3, color=green] "4"->"2" "2"->"4"[label=2, color=green] } ``` $$時空複雜度O(N)$$ ### 迭代版 ```cpp= int dp[mxN]; //mxN=100 dp[0] = dp[1] = 1; for( int i = 2; i < mxN; ++i ){ dp[i] = dp[i-1] + dp[i-2]; } ``` $$時空複雜度O(N)$$ <font color = white> $$$$ </font> ### 最常共同子序列 $a \, =\,accd$ $b \, =\,adcc$ $dp[i][j] \implies a字串取到第i個,b自串取到第j個時的LCS$ { $$ dp[i][j] = \begin{cases} dp[i-1][j-1]+1\quad &if \quad a_i = b_j \\ max( dp[i][j-1], dp[i-1][j]) \quad &else \end{cases} $$ } 把下方code中註釋掉的那兩行拔掉即可 ### 最常連續共同子序列 $a \, =\,accd$ $b \, =\,adcc$ $dp[i][j] \implies a字串取到第i個,b自串取到第j個時的LCS$ { $$dp[i][j] = \begin{cases} dp[i-1][j-1]+1\quad &if \quad a_i = b_j \\ \end{cases}$$ } ```cpp= constexpr int mxN = 100002; int dp[2][mxN]; void solve(string &s1, string &s2) { memset( dp, 0, sizeof(dp)); s1 = "+"+s1; // + has no mean s2 = "+"+s2; const int s1_len = s1.size(), s2_len = s2.size(); int mx = 0; for( int i = 1; i < s1_len; ++i) { for( int j = 1; j < s2_len; ++j ){ if( s1[i] == s2[j] ) dp[i&1][j] = dp[~i&1][j-1]+1; //i&1 i%2, ~i&1 (i+1)%2 else dp[i&1][j] = 0; mx = max( dp[i&1][j], mx); //else [with no continue] // dp[i&1][j] = max( dp[i&1][j-1], dp[~i&1][j] ); } //for( int _ = 1; _ < s2_len; _++ ) cout << dp[i&1][_] << ' '; //cout << endl; } cout << mx << '\n'; } ```