1. 程式人生 > 實用技巧 >2020動態規劃刷題

2020動態規劃刷題

動態規劃:

1、洛谷P5017擺渡車(noip2018PJT3)

先瞄一眼每個人到的時間的範圍:$0\leq t \leq4\times10^6$。果然,這道題可以以時間為狀態進行$dp$

我們以發車時間做為狀態,$f[i]$表示在第$i$分鐘發了一輛車

轉移:$f[i]=min{f[i],f[j]+等待時間(j+1\to i)}$

​ $(i - 2\times m \leq j \leq i - m)$

$\because j是上次發車的時間,設k為上次發車後車回來的時間$

$\therefore 易知k=j+m$

$\because 當k小於i-m時,必定可以先發車在回來一次,答案一定更優$

$\therefore i-m \leq k \leq i~ 即 ~i-2\times m \leq j \leq i-m$

注意:這裡等待時間應該是$j+1$到$i$,因為如果在$j$時到達車站,可以坐上那部車

因為只是動態規劃就用了近$O(mt)$的時間,所以必須保證$O(1)$的等待時間查詢,這項工作我們用字首和來做,先求到這個時間點的人數總和,再求到這個時間點的到達時間總和,等待時間即為$(區間人數\times發車時間-區間時間總和)$

但是$O(mt)$不足以通過這道題,我們要繼續剪枝。

考慮此時發車時間為第$i$分鐘,若在發車前的等待時間裡(即$i - m$分鐘到$i$分鐘,前面證明了若發車等待時間超過$m$分鐘那麼答案一定可以更優)沒有任何人來,那麼$f[i]$直接等於$f[i-m]$就可以了。實現了剪枝。

2、洛谷P2679子串

令$f[n][m][k]$為$A$字串到第$n$位,$B$字串到第$m$位,總共有$k$個子串,有多少種情況。

可得$dp$方程:

$f[n][m][k] = f[n-1][m][k]~~~(上一個不選)$

$~~~~~~~~~~~~~~~+f[n-len-1][m-len-1][k-1]~$

​ $(1\leq n\leq min(n,m)-1且A中以n為結尾len為長的字串與B中以m結尾len為長的字串一致)即選一個len長的子串$

可以寫出這樣的程式:

f[0][0][0] = 1;
for (int i = 1; i <= n; i++) {
	f[i][0][0] = 1;
	for (int j = 1; j <= m; j++) {
		for (int s = 0; s <= k; s++) {
			f[i][j][s] += f[i - 1][j][s] % mod;
			for (int kk = 0; kk <= min(i, j) - 1; kk++) {
				if (a[i - kk] == b[j - kk])
					f[i][j][s] = (f[i][j][s] + f[i - kk - 1][j - kk - 1][s - 1]) % mod;
				else break;
			}
		}
	}
}

觀察到一旦子串不匹配後面更大的子串也不匹配,所以,可以用字首和求出。

f[0][0][0] = 1;
for (int i = 1; i <= n; i++) {
	f[i][0][0] = 1;
	for (int j = 1; j <= m; j++) {
		for (int s = 0; s <= k; s++) {
			f[i][j][s] += f[i - 1][j][s] % mod;
			if (a[i] == b[j])
				sum[i][j][s] = (sum[i][j][s] + sum[i - 1][j - 1][s] + f[i - 1][j - 1][s - 1]) % mod;
			else sum[i][j][s] = 0;
			f[i][j][s] = (f[i][j][s] + sum[i][j][s]) % mod;
		}
	}
}

因為$n$的值很大,會爆記憶體,觀察到$i$只會從$i-1$推得,故滾動陣列即可,在滾動過程中要記得清零$sum$陣列。

3、洛谷P1941飛揚的小鳥

這道題的細節蠻多的,具體見程式碼,在這裡只說下大致思想。

狀態轉移方程也是很好想的:設$f[n][m]$為到$n$列$m$行最少的點選次數(設左下座標為$f[0][0]$)。
$$
f[n][m]=min(f[n-1][m+down[n-1]]\~f[n-1][m-k\times up[n-1]]+k)~~~(m-k\times up[n-1]>0)
$$
一個是通過下降轉移,一個是通過上升轉移。

我們需要特判$m$為天花板的情況,因為即使在$m_{max}-up[n-1]<m'\leq m_{max}$時,我們也可以通過一次點選來使$m'$達到天花板。

這樣就能複雜度為$O(nm^2)$,不足以通過此題。

觀察到$f[n][m]=min(f[n][m],f[n-1][m-up[n-1]]+1,f[n-1][m-2\times up[n-1]]+2,......)$,找到規律,於是可以事先通過$f[n][m]=min(f[n][m],f[n][m-up[n]]+1)$來優化從下往上飛多段的情況(相當於多重揹包),就不需要像之前那樣迴圈$k$來比較了,直接$f[n][m] = min(f[n][m],~f[n-1][m-up[n-1]]+1)$即可。

將複雜度優化到了$O(nm)$,可以通過此題。

4、洛谷P1280尼克的任務

這題顯然是一個線性動規,那麼肯定是第一時間想到設$f[i]$為$1$到$i$的最大空閒時間,但是,想了一下之後發現,第$i$時刻的最大空閒時間是和後面選擇任務的持續時間的時刻有關係的,那麼,正著找肯定是不行的,我們來試一下倒著搜,經嘗試,發現是完全可行的,可以列出動態轉移方程如下:

(本時刻無任務)$f[i]=f[i+1]+1$;//繼承上一個時刻的最大空閒時間後+1

(本時刻有任務)$f[i]=max(f[i],f[i+a[sum])$//a[sum]表示在這個時刻的任務的持續時間,找出選擇哪一個本時刻任務使空閒時間最大化

5、洛谷P2701巨大的牛棚

題意:給定一個01棋盤,求其中全為1的最大正方形邊長。

方程:$f[i][j]=min(min(f[i][j-1],f[i-1][j]),f[i-1][j-1])+1;$

注意這裡是$min$,因為對於一個邊長,若與當前點相鄰的點的最大邊長小於它,顯然這個邊長是不可取的。

6、洛谷P1472奶牛家譜

有時候計算一下無用的狀態反而是有用的。

我們不妨設$dp[i][j]$表示$i$個點小於等於$j$層的方案數,那麼最終我們所需的答案就是$dp[n][k]-dp[n][k-1]$。

於是解法就顯而易見了:列舉一個$t$,表示分$t$個點給左子樹,剩下$i-t-1$(除去樹的根)分給右子樹。

方程為:$dp[i][j] = \sum_{t = 1}^{i-2}dp[t][k-1]\times dp[i-t-1][k-1]$,由於每個點的度為$0$或$2$,所以$t$每次加$2$。

7、洛谷P1052過河

這是一道路徑壓縮動態規劃,直接列出方程$f[i] = min(f[j])+is_stone[i]$ 滿足$i-t\leq j \leq i -s$。

可是觀察到總路程$1\leq L\leq10^9$,無法作為陣列下標,但石子數$1\leq M\leq 100$卻非常小。所以可以得到路徑壓縮的思想:先離散化路徑,再$dp$。

具體來說就是,當兩點間的距離$dis$大於$t$時,一定可以由$dis%t$跳過來,對於每塊石頭,都只要計算左右兩邊距離都為$t$的路徑即可。所以只需要$t+dis%t$種距離的狀態就可以表示這兩個石子之間的任意距離關係。這樣就把題目路徑壓縮成了$2\times t\times m$最多不超過$2000$,然後就可用$dp$了。又因為$dp$的終點是一個範圍而非確切的一個點,最後還要在這個範圍內取最小值

本題值得複習

8、洛谷P1879玉米田

觀察到資料範圍很小,所以考慮用狀壓$dp$。

我們可以用一個數組表示每行所有的可用草地的集合,到時候只需要判斷目標集合是否是它的子集就知道可不可行了;再用一個數組判斷每個狀態是否滿足沒有連續的兩塊草地的條件,判斷方法就是把這個二進位制數左移一位與,然後右移一位與。如果這個狀態是合法的,那麼都應該返回 $0$

然後就開始動規,在每行裡找所有狀態,如果這個狀態是合法的,且不會在貧瘠的草地上,那麼接下來開始找上一行的合法情況(上下兩行之間沒有相鄰的草地),把上一行的情況數加到這個狀態的答案裡。

最後輸出最後一行所有狀態答案的和。

9、洛谷P1896互不侵犯

這道題既然資料範圍小,所以是狀壓$dp$。

方程需假設 $f[n][k][s]$ 為到第 $n$ 行,當前用了 $k$ 個國王(若不加這個狀態則無法轉移),當前行狀態為 $s$ 的方案數($1$ 為放國王)。方程:$f[n][k][s] = \sum f[n - 1][k - popcount(s)][lasts]$ 其中 $lasts$ 為上一行可能的狀態,且該狀態不與本行衝突。我們為了優化時間複雜度,先預處理出每個狀態上一行所對應的所有可能狀態。先特判第一行的情況,答案即為最後一行所有狀態的和。

10、洛谷P1040加分二叉樹

這道題是一個區間 $dp$。$f[i][j]$ 表示以 $i$ 到 $j$ 節點作為一顆子樹的最大加分(因為中序遍歷為 $1$ 到 $n$,所以一個子樹的結點編號是連續的),另設一個 $rt[i][j]$ 記錄該區間內的根結點,以便於輸出先序遍歷。

轉移方程即是 $f[i][j]=max(f[i][k-1]*f[k+1][j]+f[k][k])$

這道題建模的思想還是比較妙的。

11、洛谷P3648/bzoj3675序列分割

這道題先要看出,答案和分割順序無關

如果我們有長度為 $3$ 的序列 $x,y,z$ 將其分為 $3$ 部分,有如下兩種分割方法:

  1. 先在 $x$ 後面分割,答案為 $x(y+z)+yz$ 即為 $xy+yz+zx$。
  2. 先在 $y$ 後面分割,答案為 $(x+y)z+xy$ 即為 $xy+yz+zx$。

設 $f[i][k]$ 為前 $i$ 個元素,分為 $k$ 塊,且最後一刀切在第 $i$ 個元素後,最大的收益。

則稍加思考,列出方程為:$f[i][k] = max_{0 \leq j < i} {f[j][k - 1] + sum[j]\times (sum[i] - sum[j]) }$

再將 $max$ 改為 $-min{- }$ 即可,發現此時符合斜率優化的要求。

複雜度 $O(nk)$。

12、HDU2196

求離每個節點最遠的點的距離。

換根 $dp$,先算出到子樹中的最長距離(注意:要求出一個 $best$ 一個 $second$)。然後再統計向上路徑中的最長距離,具體是當前節點到之前祖先的距離加上祖先到子樹中的最長距離。

13、洛谷P2657windy數

數位 $dp$ 入門題

我們用 $f[i][j]$ 表示從高到低位搜到第 $i$ 位,上一位數是 $j$ 的情況下的方案總數。我們可以處理出 $sum[A - 1]$ 和 $sum[B]$,最終答案即為 $sum[B] - sum[A - 1]$。

這道題有兩個難點,① 因為我們要求一個數以下所有符合條件的數的個數,所以我們搜尋的數允許有前導零;
② 不能超過給定那個數最高位的限制(特殊地,$solve(43278)$,如果我們順著考慮第一位為 $4$,第二位為 $3$,也就是在上限上走,那麼討論的第三位就一定 $\leq2$ (因為第三位為 $2$,而我們討論的數不能超過 $43278$))。

考慮如何轉移:記錄當前位數,上一位數,是否有前導零,是否有最高位限制。進行記憶化搜尋即可。

注意:記憶化的時候只記憶不受前導零和最高位限制的答案。

14、洛谷P2327掃雷

這道題的難點在於動態規劃方程,設 $f[i][0/1][0/1]$ 表示前 $i$ 行,這一行是否埋雷,下一行是否埋雷的方案數。

先將 $f[0][0/1][0/1]$ 全部設為 $1$。然後分類討論:
$$
when~a_i = 0:\
f[i][0][0] := f[i - 1][0][0]\
when~a_i = 1:\
f[i][0][0] := f[i - 1][0][0]\
f[i][1][0] := f[i - 1][0][1]\
f[i][0][1] := f[i - 1][0][0]\
when~a_i = 2:\
f[i][0][1] := f[i - 1][1][0]\
f[i][1][0] := f[i - 1][1][1]\
f[i][1][1] := f[i - 1][0][1]\
when~a_i = 3:\
f[i][1][1] := f[i - 1][1][1]
$$

15、洛谷P2577午餐

容易想到貪心:吃飯慢的先打飯節約時間, 所以先將人按吃飯時間從大到小排序

證明:因為所有人打飯時間是節省不了一點的,所有隻能讓吃的慢的先盛飯這樣節約時間。

下面就是 $dp$ 了,狀態和轉移都比較難想。

首先,應該想到 $f[i][j][k]$ 表示前 $i$ 個人,在 $1$ 號視窗打飯總時間 $j$,在 $2$ 號視窗打飯總時間 $k$,這樣所用最少時間。當然,這樣會爆空間,所以想到去掉一維。

原方程可以改為 $f[i][j]$ 表示前 $i$ 個人,在 $1$ 號視窗打飯總時間 $j$,所用最少的時間。

我們可以發現 $j+k=\text{前i個人打飯總和}$,$k = sum(i)-j$。 所以可以省掉一維。
$$
f[i][j] = min{max{f[i - 1][j - A[i]], j + B[i] },max{f[i - 1][j], sum[i] - j + B[i] } }
$$
前面式子表示當前人去 $1$ 號視窗打飯的情況,若這個人打飯加上吃飯的時間還不如前面人打飯加上吃飯的時間多,那就取後者,反之取前者轉移。後面式子同理,表示當前人去 $2$ 號視窗打飯,其餘一致。

16、洛谷P1850 換教室

期望 $dp$

容易想到設 $f[i][j][0/1]$ 為到了第 $i$ 次課,使用了 $j$ 次機會,當前在 $0$ 還是 $1$ 號教室,最小的期望值。

首先,根據性質,期望的和等於和的期望,我們分開討論每兩次課之間的體力值,然後加起來就是答案。
$$
f[i][j][0] = min\begin{cases}
f[i - 1][j][0] + dis[i - 1][c[i]]\
f[i - 1][j][1] + k[i - 1] \times dis[d[i - 1]][c[i]] + (1 - k[i - 1]) \times dis[c[i - 1][c[i]]\
\end{cases}
$$

$$
f[i][j][1] = min\begin{cases}
f[i - 1][j - 1][0] + k[i] \times dis[c[i - 1]][d[i]] + (1 - k[i]) \times dis[c[i - 1]][c[i]]\
f[i - 1][j - 1][1] + k[i] \times(k[i - 1] \times dis[d[i - 1]][d[i]] + (1 - k[i - 1]) \times dis[c[i - 1]][d[i]])\

  • (1 - k[i]) \times (k[i - 1] \times dis[d[i - 1]][c[i]] + (1 - k[i - 1])) \times dis[c[i - 1]][c[i]]
    \end{cases}
    $$

17、hdu3001

這道題是三進位制狀壓的模板題

設 $f[i][s]$ 為現在在 $i$ 點,目前訪問的節點情況是 $s$(每一位 $0$ 表示沒有訪問過,$1$ 表示訪問過一次,$2$ 表示訪問過兩次)

狀態轉移就是 $f[j][s + {j}] = min{f[i][s] + dis[i][j]}$ (當且僅當 $s$ 中 $j$ 點訪問次數小於 $2$)。考慮初始化,使用 $bit[i]$ 先將訪問每個點一次的狀態寫出來($3^i$),於是 $f[i][bit[i]] = 0$ 即可。

三進位制無法像二進位制一樣使用位運算來取得每一位的值,於是初始化 $digit[s][i]$ 表示狀態 $s$ 中第 $i$ 位的數,這樣就可以像二進位制一樣判斷了。

注意:$dp$ 中先列舉 $s$ 再列舉 $i, j$。

18、洛谷P4401礦工配餐

這道題妙在設 $dp$ 方程上。設 $f[i][a][b][c][d][0/1]$ 表示到了第 $i$ 個餐車,$a、b$ 為不包括當前這個餐車送往 $1$ 號礦場的餐車前一個為 $a$ 類車,再前一個為 $b$ 類車(若型別為 $3$ 則說明沒有),$c、d$ 為送往 $2$ 號礦場的餐車,定義同理。最後一維則表示當前餐車送往 $1$ 號礦場還是 $2$ 號礦場。

可以列出方程:
$$
f[i][a][b][c][d][0] = max\begin{cases}
f[i - 1][b][x][c][d][0] + (a, b, x[i])& 當a恰好等於x[i-1]或為3\
f[i - 1][a][b][d][x][1] + (a, b, x[i])& 當c恰好等於x[i-1]或為3\
\end{cases}\
f[i][a][b][c][d][1] = max\begin{cases}
f[i - 1][a][b][d][x][1] + (c, d, x[i])& 當c恰好等於x[i-1]或為3\
f[i - 1][b][x][c][d][0] + (c, d, x[i])& 當a恰好等於x[i-1]或為3\
\end{cases}\
\forall x\in [0, 3]
$$
還有一種做法是設 $f[i][a][b][c][d]$ 表示包括當前餐車的結果,簡便一些。

19、洛谷P5888

設 $f[n][m]$ 為經過 $m$ 次傳到 $n$ 號的方案數,容易列出狀態轉移方程 $f[n][m] = \sum\limits_{i\text{可以傳給n}}f[i][m - 1]$。
可是這樣發現 $n$ 的大小太大,無法轉移。於是考慮只求傳到受到限制的點的方案數,因為不受限制的都可以被剩下 $N - 1$ 個人傳到即可以直接求出。具體地,離散化一下受到限制的人的號碼(把 $1$ 號也加入,因為方便直接輸出 $1$ 號的 $dp$ 值作為答案),列舉第 $i$ 次傳球,求出第 $i$ 次傳球后不受限制的總方案數(上一次傳球后受限制的總方案數 $\times(n - 1)$),則 $f[n][m]$ 為不受限制傳到 $n$ 的方案數(上一次傳球后受限制的總方案數 $-f[n][m - 1]$ 即可以傳到 $n$ 的方案數 $\times 1$)$-\sum\limits_{i\text{不能傳給n}}f[i][m - 1]$。同時可以計算出本次傳球后受限制的總方案數。

這題是一道很好的正難則反的動歸題

20、洛谷P6478 遊戲

首先,我們設 $f[u][k]$ 表示在 $u$ 的子樹下,選擇一共 $k$ 對不平局的點,有多少種情況,很顯然轉移式子可以列為:
$$
f[u][k] = \sum_{k_1+k_2+\cdots+k_n = k} f[v_1][k_1]\times f[v_2][k_2]\times \cdots\times f[v_n][k_n]
$$
最後將當前子樹的根節點 $u$ 和下面不同型別的點進行匹配,譬如當 $u$ 為 $A$ 集合中的點,$B[u]$ 表示 $u$ 下有多少 $B$ 集合的點,那麼有 $f[u][k + 1] = f[u][k] \times (B[u] - k)$。

如此,我們能夠 $O(n^2)$ 求出陣列值,設 $F(k) = f[1][k] \times(\dfrac{n}{2}-k)!$,後面乘以一個剩餘點數的階乘,表示大於等於 $k$ 對的所有情況。然而,我們能夠發現這個 $F(k)$ 其實是有重複的,我們有兩種方式解決這個問題:

  • 設 $ans(k)$ 表示恰好有 $k$ 対的情況數,那麼,根據二項式反演,可得:

$$
F(k) = \sum_{i=k}^{\frac{n}{2}} {i\choose k} ans(i) \iff ans(i) = \sum_{k = i}^{\frac{n}{2}} (-1)^{k - i}{k \choose i} F(k)
$$

  • 同樣是上面的邏輯,發現每個 $ans(i)$ 只與 $ans(j)$ 其中 $j\ge i$ 有關,顯然我們可以倒序求出每個 $ans(i)$,完全不需要二項式反演

21、poj3783 鷹蛋

這題先給出樸素演算法,然後引出一個更優的套路演算法。

演算法一:設 $f[n][m]$ 表示 $n$ 層樓,$m$ 顆蛋,至少需要多少次才能檢測出臨界值,那麼容易有如下轉移方程式:
$$
f[n][m] = \min_k{\max{f[k - 1][m - 1], f[n - k][m] }+1 }
$$
在這個演算法中,我們列舉 $k$ 層樓作為臨界值,然後向上或者向下繼續尋找,時間複雜度 $O(n^2m)$

演算法二:設 $f[m][k]$ 表示現在有 $m$ 顆蛋,扔 $k$ 次,最多能夠確定多少層樓的臨界值,那麼可以獲得轉移:
$$
f[m][k] = f[m - 1][k - 1] + (f[m][k - 1] + 1)
$$
如果蛋碎了,那麼剩下的蛋能確定的層數是第一項,如果沒碎就是第二項。隨後只需要在求答案時二分出這個 $k$ 即可。

22、洛谷P6622 [省選聯考 2020 A/B 卷] 訊號傳遞

這道題可以先設 $f[n][S]$ 表示前 $n$ 個位置,使用了 $S$ 這些訊號站,答案最小為多少。

則容易有 $f[n][S \bigcup {x }] = \max{f[n - 1][S] + g[x][S] }$,其中 $g[x][S]$ 表示在前面位置用了 $S$ 訊號站的情況下,當前位置用 $x$ 訊號站對答案的貢獻。觀察到 $g[x][S]$ 只可能從 $S$ 中 $1$ 個數少的更新到個數多的,於是可以壓縮狀態。具體地,我們這裡可以使用一個經典結論:二進位制從 $0$ 數到 $n$,所有位元位變化的總次數為 $O(n)$。我們直接去掉 $g$ 的 $S$ 那一維。然後按二進位制數大小順序列舉 $S$,並按照 $S+1$ 相對 $S$ 的變化暴力修改 $g$。根據上述結論,我們對每個位元位的變化,修改 $g$ 的代價是 $O(m)$。那麼總修改代價就為 $O(m2^m)$。

23、[USACO11FEB]Best Parenthesis

本題和括號樹的思路很像,考慮維護一個 $f[n]$ 表示以 $n$ 為右端點的子段的值是多少,使用棧來維護即可。

本題還有一個巧妙的做法,考慮分治計算,記 $solve(l, r)$ 表示 $[l, r]$ 的答案,若 $l, r$ 恰好匹配,則 $solve(l + 1, r-1)$,否則分治下去。

24、洛谷P2592 [ZJOI2008]生日聚會

設 $f[n][m][x][y]$ 表示有 $n$ 個男孩,$m$ 個女孩,字尾中男孩比女孩多 $x$ 人,女孩比男孩多 $y$ 人有多少種情況。
$$
f[n + 1][m][x+1][\max(y-1, 0)] = \sum f[n][m][x][y]\
f[n][m + 1][\max(x-1,0)][y+1]=\sum f[n][m][x][y]
$$
注意本題用當前更新下一狀態比較容易

25、洛谷P3622 動物園

這是一道隱藏的非常深的狀壓 $dp$ 題,我們注意到每個小朋友能觀察到的區間長度只有 $5$,於是可以考慮將動物保留與否壓縮成二進位制。我本來想是對於每個小朋友進行 $dp$,但這樣實現太麻煩了,不如先預處理出以每個位置開始的五個動物保留與否能使多少小朋友高興,然後再按照位置 $dp$,在 $dp$ 過程中,我們會發現最後幾個位置的狀態會回到剛開始的狀態,因此我們先列舉剛開始的狀態再 $dp$,時間複雜度 $O(2^{10}n)$

26、uva1252 twenty questions

這是一道很好的狀壓 $dp$ 題,狀態的設計十分巧妙。

假設當前詢問的是 $W$,設 $f[S_1][S_2]$ 表示目前已經詢問了 $S_1$ 位置,確定了 $S_2$ 在 $W$ 中,還需要詢問的次數。
$$
f[S_1][S_2] = \min_k \max{ f[S_1\cup {k}][S_2\cup {k}], f[S_1\cup{k}][S_2]}
$$
邊界情況是當只有一個物品滿足具備 $S_2$ 特性卻不具備 $S_1-S_2$,此時已經能夠判斷出來了,$f[S_1][S_2] = 0$。於是我們只需要預處理 $cnt[S_1][S_2]$ 表示有多少物體滿足條件即可。

27、uva1439 exclusive access

本題題意不是特別好懂。簡要題意:給定一張無向圖,要求將無向邊定向,使得 $DAG$ 的最長路最短。

我們可以轉化一下模型,將 $DAG$ 剝為一層一層的,這可以理解為圖的色數問題,即將一張無向圖黑白染色,要求相同顏色的點之間沒有邊,且顏色數最少。這個經典模型可以用狀壓 $dp$ 解決,時間複雜度 $O(3^n)$。現在考慮如何輸出方案,只要記錄每個點的層數,然後只允許層數低的連向層數高的。