CSP2019 演算法總結 DP

2022-05-05 16:12:09 字數 3612 閱讀 9579

#區間$dp$

題目清單:

!(#狀態設計

$1.$對於這一類問題,一般設計狀態為$dp[i][j]$,表示完成$i-j$區間的答案。

$2.$有一些奇葩的情況,可以這樣設計狀態:$dp[i][j]$表示完成$j$時,左端點為$i$,右端點為$j-1$

#狀態轉移

思想是用已經更新的小區間擴充套件到未被更新的大區間,關鍵在於如何更新,如何擴充套件

一種辦法是列舉斷點把乙個大區間劃分成小區間

另一種辦法是奇葩的倍增思想[傳送門](

有三種\(dp\)順序

\(1.\)先列舉區間長度,再列舉左端點,再列舉斷點,因為區間長度從小到大,所以子狀態一定都被更新,切忌先列舉左端點。

\(2.\)倒序列舉,即左端點(\(i\))\(n->1\),右端點(\(j\))\(i->n\),斷點\(i-->j\)。

\(3.\)記憶化搜尋,這樣做無需考慮列舉順序

\(4.\)倍增思想,\(dp[i-1][dp[i-1][j]]\),見上面鏈結

一、四邊形不等式

使用範圍:區間序列\(dp\)求最小值(一定是最小值)

對於動態規劃轉移方程

dp[i][j]=min(dp[i][k],dp[k+1][j])+w(i,j);
其中\(w(i,j)\)只受\(i,j\)取值影響

如果滿足下面兩個條件

\(1.\)區間單調性:如果對於\(\forall i \leq i'< j' \leq j,w(i',j') \leq w(i,j)\)(即小區間取值\(\leq\)大區間取值)

\(2.\)四邊形不等式:\(\forall i \leq i'< j' \leq j,w(i,j)+w(i',j')\leq w(i',j)+w(i,j')\)

即中的紅線總長\(\geq\) 藍線總長

如果\(w(i,j)\)同時滿足區間單調性和四邊形不等式,那麼\(f(i,j)\)滿足四邊形不等式

令\(s(i,j)\)為\(f(i,j)\)在取到最優解時的決策點\(k\)

那麼決策本身具有單調性,即滿足\(s(i,j)\leq s(i,j+1)\leq s(i+1,j+1)\)

用\(j\)代替\(j+1\)得到

\(s(i,j-1)\leq s(i,j)\leq s(i,j+1)\)

轉移方程變為

\(f(i,j)=min(f(i,k)+f(k+1,j))+w(i,j);\ (s(i,j-1)\leq k\leq s(i+1,j))\)

可以證明,他將時間複雜度降到了\(o(n^2)\)

什麼時候使用四邊形不等式?

只需要牢記公式

\(s(i,j-1)\leq s(i,j)\leq s(i,j+1)\)

考試時可以打一張決策表看是否滿足上面式子,滿足可以使用四邊形不等式

\(1.\)序列\(dp\)有時可以使用四邊形不等式優化,但僅僅是常數優化

\(2.\)需要注意四邊形不等式僅針對求最小值的情況

題目清單:

對於這一類\(dp\),一般設計兩維狀態,\(dp[i][j]\)表示當前節點為\(i\),子節點或相鄰節點的狀態為\(j\)時的答案

\(1.\)樹形結構天然的\(dfs\)序保證了更新當前節點時子節點已經被更新完畢,所以就把\(dp\)過程放到\(dfs\)遍歷中即可,注意初始化。

一般每搜尋一顆子樹回溯回來就更新答案,最後在迴圈外面向上回溯前自己更新自己答案

\(2.\)當然還有種狀態轉移是需要所有相鄰節點的狀態,對於這種問題直接\(for\)迴圈列舉所有點即可,但要注意列舉順序,是否會出現狀態沒有的情況,用多維迴圈內層列舉當前點,最內層列舉相鄰點

題目清單

狀壓\(dp\)是一類非常特殊的\(dp\),基本思想是把\(dp\)的狀態壓縮為二進位制等,以減小空間且保證轉移正確性

一般看到乙個題資料範圍是\(n<=18\)且是動態規劃,則大概率是狀壓\(dp\)

狀態設計時,一維用二進位制記錄狀態,一般在\((1左右,如果需要可以設計另一維表示當前位置,對於很多與選擇順序無關的題目這一維可以省略。

也有時候第二維可以分割為另乙個\(dp\)陣列,大大優化空間

\(1.\)位運算的使用:位運算可以提高效率且減少**複雜度

s<<=1 //左移*2

s>>=1 //右移/2

(s&(1

重點在於考慮記錄哪些狀態

一般一維列舉前乙個狀態(一般是最外層),另一維列舉轉移什麼(一般是乙個值);少數情況第二維列舉接下來狀態,因為這樣複雜度極大,一般需要剪枝;極少數情況很毒瘤,表面上看起來只能列舉兩種狀態,但可以拆到另外乙個\(dp\)陣列,變為第一種轉移方式,比如裝箱問題,要求數量最小,可以用乙個\(dp\)記錄最後乙個箱子剩餘空間,從而\(o(sn)\)轉移,不過要注意輔助\(dp\)陣列被更新的前提是正常\(dp\)陣列能被更新,是乙個依賴關係,也就是在保證第一答案最優時第二答案最優,這樣是對的

傳送門\(3.\)優化問題

\((1)\)很多無用狀態可以不列舉,比如炮兵陣地問題,可以預處理每一行的合法情況裝進\(vector\),\(dp\)時只需要列舉這些情況,大大降低複雜度。

\((2)\)還有一類問題:憤怒的小鳥,在所有已有狀態中列舉找到乙個值,再列舉不在狀態中的值,複雜度是\(o(n^2)\),但最優解與選擇順序無關,所以可以控制每次選擇最小位置更新,複雜度\(o(n)\)

\((3)\)對於區間、字串操作,可以壓縮狀態後連邊,跑最短路求解

\((4)\)與考慮順序無關的\(dp\)可以優化掉一維,只與附近固定位置有關,可以使用滾動陣列優化

一定要小心mle!!!!!

題目清單(做的比較少)

一般資料範圍為\(10^\)

通常使用多維,每維規模比較小。

如\(dp[i][j][k][0/1]\)表示當前在第\(i\)位(從最低位到最高位編號),當前位置是\(j\),附近位置的狀態是\(k\),是否滿足題目要求條件時的方案數(一般記憶化搜尋\(k\)表示前面狀態,遞推\(dp\ k\)表示後面狀態,記憶化搜尋還需要記錄是否小於邊界)

數字\(dp\)一般有兩種做法:\(1.\)遞推計數,\(2.\)記憶化搜尋,兩者的核心思想:逼近法,逐位確定是一樣的

繼續回到狀態轉移上

對於遞推計數的方法,首先要預處理\(dp\)陣列(本身是數字\(dp\)),再按照逼近法計數,狀態多的時候預處理會很麻煩,需要邊界\(+1\)

對於記憶化搜尋,只需要邊搜尋邊記錄\(dp\)陣列即可,對於這種方法,狀態轉移比較簡單,碼量小,比較容易思考,邊界不需要提前\(+1\)

\(1.\)遞推計數預處理時有時可以用字首和等等方法優化

\(2.\)初始值為\(-1\)時,小心陣列下標越界

\(3.\)看好題目範圍中是否包含\(0\)

\(4.\)前導零情況單獨處理

(CSP2019模擬)閱讀

有n nn個字串,每次可以把每個字串都標記至多乙個字首,但這些字首之間不能有包含關係,求把所有字串的所有字首都標記完的最小次數。資料範圍 n nn,字串總長 100000 le100000 10000 0 如果乙個字串s ss的字首a aa包含於另乙個字串t tt的字首b bb,那麼a aa是t t...

CSP2019初賽遊記

沒想到居然ak了,就紀念一下吧。這次的聯賽,只能說是高三生活的一縷雜音了吧。這次,我的心態可以說是非常平穩了。畢竟不再會有競賽生涯的壓力,也不會有患得患失的惶恐。不過也許我開考前的放聲唱歌驚擾了一些人,對不起了。笑 許多曾同在機房,現在同在教室的同學們,做完之後就趴下了。雖然我覺得確實挺簡單的,但是...

CSP2019退役遊記

本文同步至github 試題答案 謹以此紀念我的oi生涯。晚上xy發表重要講話,保送名額乙個都沒有。預示要涼 早晨6 00起床,本以為在家會睡得好一點確實好一點,在學校會有一堆延遲熄燈的人。然而還是頭暈。準時到校,坐校車,我們被分到和西溪高三一起坐,我左前方是zzy和zrf,只見他們在打雀魂。果然是...