蒟蒻亂寫一通關於動態規劃幾類問題的筆記,可能會有錯誤之處,歡迎指正。
一. 01揹包問題
關於這個問題,我之前已經寫了不太全面的(比較扯淡的)筆記,就不複述了。
傳送門:揹包問題學習筆記
補充一下除了01揹包、完全揹包、多重揹包外,還有乙個超大揹包問題值得了解。
二. 最長上公升子串行問題(lis)
推薦題解:動態規劃——最長上公升子串行問題
題目不贅述了,lis就是最長上公升子串行。簡單來說,就是在一串給定的數列a[n]中取出一些數(未必要連續),讓它們能單調上公升,並且這個數列要最長。
舉個例子,對於長度為10的數列「1,9,11,2,10,7,8,9,13,6」,它的lis就是「1,2,7,8,9,13」,長度為6。
對於這個問題,有兩種演算法,複雜度分別為o(n2)和o(nlogn)。雖然我們發現o(n2)的演算法是無法ac洛谷的lis板子題的,但是o(n2)的演算法思想仍然有助於我們理解動態規劃。
o(n2)的經典演算法:
根據動態規劃把大問題拆成小問題,分段求解的思路,我們宣告乙個陣列f[maxn],f[i]表示從1到i中,以a[i]結尾的最長上公升子串行的長度。初始時f[i]=1,i∈[1,n]。(初始值其實就是這個序列中只有a[i]時的序列長度,顯然為1)。
可以寫出狀態轉移方程:f[i]=max, j∈[1,i-1]且a[j]
怎麼理解這個方程呢?就是說,當我們已經處理完了f[i-1],需要求f[i]時,只需要遍歷一遍a[1…i-1],找到所有能成為a[i]前驅的數a[j](即a[i]>a[j]),然後在所有能成為前驅的a[j]中找到f[j]最大的那個就可以了。如果還不理解,可以嘗試直接看**。
因為**是寫出來便於理解的,我就不寫暫存器內聯快速讀入之類花裡胡哨的東西了嘻嘻嘻。
#include usingo(nlogn)的優秀演算法:如果想優化上面的演算法,基於貪心的思想,我們很容易想到:當x,y∈[1,i-1]時,若f[x]=f[y],a[x]namespace
std;
const
int maxn=100000
;int n,a[maxn+5],f[maxn+5
];int
result;
intmain()
for (int i=1;i<=n;i++)
for (int j=1;j)
if (a[i]>a[j]&&f[i]1
) f[i]=f[j]+1
;
for (int i=1;i<=n;i++)
if (resultf[i];
printf("%d
",result);
return0;
}
所以在f[x]一定的情況下,盡量選擇更小的a[x]。
按f[x]=k來分類,我們需要記錄的當所有等於k的f[x]中,最小的a[x]。我們宣告乙個low[k]來儲存這個最小的a[x]。
這樣說可能會有點亂,簡單說吧,就是宣告乙個low[k],儲存在[1,i-1]之間,已知的最長上公升子串行長度為k的最小的a[x]值。(還是感覺比較複雜,將就理解一下吧)
low[k]=min,f[x]=k;
可以歸納出low[k]的幾個性質:
①low[x]單調遞減增,即low[1]<low[2]<low[3]<low[4]<……<low[n-1]<low[n];
②隨著處理時間推進,low[x]只會越來越小;
如果不能理解,可以嘗試自己寫個數列模擬看看。
有了這兩個性質,就可以這樣求解:
宣告當前已求出的最長上公升子串行的長度為len(初始時為1),當讀入乙個新元素x:
①若x>low[len],則直接把x加入到d的末尾,且len+=1;
②否則,在low[x]中二分查詢,找到第乙個比x小的數low[k],並low[k+1]=x,在這裡x<=g[k+1]一定成立。
易證時間複雜度為o(nlogn)。
**中的二分查詢我用stl的lower_bound函式代替了,但是不開o2會慢挺多吧……手寫二分應該會快,蒟蒻我太懶了orz
#include #include三. 最長公共子串行問題(lcs)注意,這次是最長公共子串行(lcs)。lcs就是指給定兩個數列,兩個數列中最長的公共子串行(哇我在說什麼廢話)。using
namespace
std;
const
int maxn=100000
;int n,len=1
;int a[maxn+5],low[maxn+5
];int
main()
printf("%d
",len);
}
舉個例子好了,比如下面兩個長度分別為6的子串行:
1 4 9 10 2 6
2 1 10 2 13 6
上面兩個子串行,它們的lcs就是長度為4的序列: 1 10 2 6 。和lis一樣,子串行是不需要連續的。
為了解決這個問題,我們可以嘗試這樣思考:
首先,記給定的兩個序列為s和t,依舊是根據動態規劃分段求解的思想。
定義f[i][j]為序列 s1…si 和序列 t1…tj 對應的lcs的長度。
那麼f[i+1][j+1]有三種情況:
① si+1=ti+1時,在序列 s1…si 和序列 t1…tj 對應的lcs後面追加si+1(si+1=ti+1);
② 繼承序列 s1…si 和序列 t1…tj+1 對應的lcs;
③ 繼承序列 s1…si+1 和序列 t1…tj 對應的lcs;
f[i][j]為上面三種情況中最大的乙個。所以可以寫出遞推式:
這個遞推式可以在o(n2)的時間內被計算出來,f[n][n]是lcs的長度。
動態規劃例題
題目描述 有一條很長的數軸,一開始你在0的位置。接下來你要走n步,第i步你可以往右走ai或者bi。問n步之後,0到m的每個位置,能不能走到?輸入格式 第一行,兩個整數n,m。接下來n行,每行兩個整數ai,bi。輸出格式 一行,一共m 1個數,每個數都是0或1表示能否走到,數字之間不用空格隔開。樣例輸...
《動態規劃》 ACM 動態規劃例題詳解
描述 7 3 8 8 1 0 2 7 4 4 4 5 2 6 5 圖1 圖1給出了乙個數字三角形。從三角形的頂部到底部有很多條不同的路徑。對於每條路徑,把路徑上面的數加起來可以得到乙個和,你的任務就是找到最大的和。注意 路徑上的每一步只能從乙個數走到下一層上和它最近的左邊的那個數或者右邊的那個數。輸...
動態規劃及其動態規劃經典例題
動態規劃是最重要 最經典的演算法之一,學好動態規劃對我們十分重要,掌握動態規劃對解決某些問題會起到事半功倍的效果。特點 可以把原始問題劃分為一系列子問題 求解每個子問題僅一次,並將其結果儲存到乙個表中,以後用到時直接訪問,不重複計算,節省時間。自底向上地計算 適用範圍 原問題可以分為多個相關子問題,...