我對本文的定位是題型. 高手請忽略.
本文會隨時進行必要的更新, 如果你有一些經典題目和一些經典題型, 請您一定告訴我喲~
部分摘自黑書.
動態規劃
動態規劃的兩種動機:
1. 利用遞迴的重疊子問題,進行記憶化求解,即先用遞迴法解決問題,再利用重疊子問題轉化為動態規劃.
例,
1)括弧序列
2)棋盤分割
3)決鬥
2. 把問題看成多階段決策過程.
例,
1)舞蹈家懷特先生
2)積木遊戲
可以通過增加維數的方法來消除後效應, 也可以在保持無後效應性的前提下改變狀態定義,以得到更多的重疊子問題.
當問題不具備最有子結構時,通常通過增加維數的辦法來解決.
常見模型:
1. 線性模型
例,
1)方塊消除
2)公路巡邏*
3)並行期望值*
4)高效能運算*
2. 串模型
1)模板匹配(常為s[i][j] 和 s[i][j-1] s[i-1][j]的關係)
2)不可分解的編碼*
3. 區間模型
1)青蛙的煩惱(常為d[s][L][x]從s起長度為L的一個區間, 對於某些特定問題,x可以用來選擇區間的起點)
2)排列問題
3)最優排序二叉樹*
4.狀態壓縮模型
1)Bugs公司*
2)迷宮統計*
3)貪吃的九頭蛇*
我的分類:
一. 遞迴向下分解成更小的問題, 轉化為動態規劃向上
1. 分蘋果問題(pku 1664)
2. 整數分割問題(王曉東<<演算法分析與設計>>)
3. 買書問題(編程之美1.4可以看到這道題)
二. 背包問題
貼一份原始碼:
#include <iostream><br />#include <algorithm><br />using namespace std;</p><p>#define N 4<br />#define W 6</p><p>int opt[N+1][W+1]; //i:0~N-1, j:0~V<br />int w[N+1] = {1,2,3,4};<br />int v[N+1] = {0,2,4,5};</p><p>//用後i-N+1個物品(i,i+1,...N-1), 包的容量為j時的最大價值.</p><p>//寫法一, 較麻煩<br />int knapsack()<br />{<br />int tmp = min(w[N-1], W);<br />for(int i = 0; i < tmp; ++i)<br />opt[N-1][i] = 0;<br />for(int i = w[N-1]; i <= W; ++i)<br />opt[N-1][i] = v[N-1];</p><p>for(int i = N-2; i >= 0; --i)<br />for(int j = 0; j <= W; ++j)<br />{<br />opt[i][j] = opt[i+1][j];<br />if(j >= w[i])<br />{<br />int value = opt[i+1][j-w[i]] + v[i];<br />if(value > opt[i][j])<br />opt[i][j] = value;<br />}<br />}<br />return opt[0][W];<br />}</p><p>//寫法二, 稍微簡略<br />int knapsack2()<br />{<br />for(int i = 0; i <= W; ++i)<br />opt[N][i] = 0;</p><p>for(int i = N-1; i >= 0; --i)<br />for(int j = 0; j <= W; ++j)<br />{<br />opt[i][j] = opt[i+1][j];<br />if(j >= w[i])<br />{<br />int value = opt[i+1][j-w[i]] + v[i];<br />if(value > opt[i][j])<br />opt[i][j] = value;<br />}<br />}<br />return opt[0][W];<br />}</p><p>//節省空間的<br />int space_opt[2][W+1];<br />int space_opt_knapsack()<br />{<br />int tmp = min(w[N-1], W);<br />for(int i = 0; i < tmp; ++i)<br />opt[0][i] = 0;<br />for(int i = w[N-1]; i <= W; ++i)<br />opt[0][i] = v[N-1];</p><p>int cc = 1;<br />for(int i = N-2; i >= 0; --i)<br />{<br />cc = 0?1:0;<br />for(int j = 0; j <= W; ++j)<br />{<br />opt[(cc+1)%2][j] = opt[cc%2][j];<br />if(j >= w[i])<br />{<br />int value = opt[cc%2][j-w[i]] + v[i];<br />if(value > opt[(cc+1)%2][j])<br />opt[(cc+1)%2][j] = value;<br />}<br />}<br />}<br />return opt[cc][W];<br />}</p><p>//備忘錄法(又稱記憶化搜尋), 不需要計算的不做計算<br />int f(int i, int j)<br />{<br />if(i == N)<br />return 0;</p><p>if(opt[i][j] != -1)<br />return opt[i][j];</p><p>if(j >= w[i])<br />return max(f(i+1, j-w[i])+v[i], f(i+1, j));<br />else<br />return f(i+1, j);<br />}</p><p>void knapsack_recursion_test()<br />{<br />memset(opt, -1, sizeof(opt));<br />cout << f(0, W) << endl;<br />}</p><p>int main()<br />{<br />knapsack_recursion_test();<br />return 0;<br />}
雜湊法
一. 直接雜湊, 相當於打表, 不再多言
二. 直接雜湊的空間最佳化
當某些元素已經不必要時, 我們將其刪除, 以減小空間的消耗
比如, 在幾G空間中, 只有一個數出現了一次, 其他都出現了兩次, 對其進行hash, 每當一個ID出現兩次後, 可以將其空間釋放.
異或運算的運用
一. 利用異或尋找數列中出現奇數次的數
問題: 在幾G空間中, 只有一個數出現了一次, 其他都出現了兩次, 找出這個數?
因為有A^A=0 A^0 = A, 我們可以把所有數做異或運算, 所以我們可以找出這個數.
問題: 在幾G空間中, 所有數都出現了兩次, 結果丟失了兩個數, 如何找出這兩個數A, B?
我們可以把所有數做異或運算, 如果結果為0, 那麼說明丟失的是兩個相等的數(A=B);
否則, 丟失的是不同的兩個數, 所有數的異或不為0, 其結果必有一位為1, 那麼A,B有且就有一個的這一位也為1.
我們把所有數根據這一位分為兩類, 一類這位為0, 一類這位為1,假設第一類含A, 第二類含B, 那麼第一類所有數的異或值必為A, 第二類所有數的異或值必為B.
待續...