DP 從入土到入門

emm隨便錯

樹形DP

例題

沒有上司的舞會

沒有上司的舞會
定義 \(f[x][0/1]\) 表示x這個點選或者不選能夠帶來的最大的快樂值。

因爲每一個人的頂頭上司肯定不能和他一起選,也就是說,如果當前這個人選了,那麼他的直接下屬不能選,但是當前這個人不選對快樂值沒有影響,也就是。

\[\begin{cases} f_{x, 1} = f_{to, 0} \\ f_{x, 0} = \text{max}(f_{to, 1}, f_{to, 0}) \end{cases} \]

然後題目就做完了,一個 dfs 解決問題

[HAOI2009]毛毛蟲


題面自己看吧,簡化不動...


傳送門
我們令 \(f_x\) 表示以 x 爲根的子樹內最大的毛毛蟲的大小,而且 x 爲毛毛蟲的頭,那麼 \(f_x\) 的轉移方程就很顯然了。
\(cnt_x\) 爲 x 的兒子大小。
\(f_x = \max_{to \in son_x} f_{to} + 1 + \max(0, cnt_{to} - 1)\)

[ZJOI2007]時態同步

傳送門


給你一棵樹,邊有邊權,然後問你每次可以將一條邊的邊權加一,
問你至少多少次操作之後,1 這個根節點到葉子節點的路徑長度都相同。


可以讓所有葉子節點同時發出信號,然後這些信號同時到達根節點。於是我們可以自下而上的進行維護,使得每一節點所有子節點的信號同時到達該節點。

我們從根節點開始搜索,搜索到葉子節點,回溯的時候進行維護,先維護節點的所有子節點到該節點最大邊權(邊權爲葉子節點到同時到達它所需要時間)。然後維護答案,答案爲最大邊權減去所有到子節點的邊權。然後維護父節點的邊權,父節點邊權爲該節點子節點的 最大邊權+父節點到該節點的時間。然後就回溯,重複操作,到根節點爲止。

換根法

也叫二次掃描法
基本思路是先指定一個根結點,然後第一次dfs求出根節點的權值,然後第二次dfs的時候可以搞一個轉移方程由根結點轉移過去。

STA-Station

傳送門


給定一個 \(n\) 個點的樹,請求出一個結點,使得以這個結點爲根時,所有結點的深度之和最大。
一個結點的深度之定義爲該節點到根的簡單路徑上邊的數量。


\(dp_i\) 爲第 i 個結點作爲根節點時的深度之和,我們不妨令根結點爲 1,然後進行第一次dfs,求出根結點爲 1 時的深度之和,然後考慮如何轉移。

以樣例爲例

8
1 4
5 6
4 5
6 7
6 8
2 4
3 4


我們可以知道 \(dp_4\)\(12\) 考慮如何換到5這個結點。

因爲換成 5 的時候相當於 5 所在的子樹所有的結點的深度都減一,剩下的結點的深度都加一,所以我們令 \(siz_x\) 記錄 x 這個結點的子樹的大小,\(point\) 爲所有子樹的大小,然後\(x, to\) 爲 x 結點與 x 結點所到的 to( x 爲 to 的父親結點)。
那麼 \(point - siz_{to}\) 爲除了以 to 爲根的子樹之外的有多少個點。

\[dp_{to}=dp_x+(point-siz_{to}) - siz_{to} \]

Great Cow Gathering G

傳送門


給你一棵樹,邊有邊權,讓你求出一個到其他所有點的路徑和最小的那個點的路徑和。


和上一個題差不多,就是每每條邊有邊權,也就是說我們可以通過讓 siz 數組維護的值改變一下來 AC 這道題。

考慮 siz 數組中存貯每個牛棚的以及他的子樹中,一共有多少頭牛,剩下的轉移就和上邊那個題差不多了。

Choosing Capital for Treeland

傳送門


給你一棵樹,但是樹上的邊都是單向邊,問以 i 點爲根的時候,爲了能夠遍歷到每個點,需要修改多少條邊的方向。


建圖的時候按照雙向邊建圖,正向和反向邊分別標記,然後可以很輕鬆的求出 1 爲根時的答案,轉移的時候就判斷 x 與 to 之間連的邊是正向邊還是反向邊就行了。

Nearby Cows G

傳送門
給你一棵 \(n\) 個點的樹,點帶權,對於每個節點求出距離它不超過 \(k\) 的所有節點權值和 \(m_i\)


我們用 \(f_{i,j}\) 表示距離 i 點爲 j 的所有點的點權和,可以很容易得到下邊的遞推式

\(f_{i,j} = \sum f_{x,j-1}\),其中 x 爲 i 的兒子結點,第一遍 dfs 的時候求出這個即可。

考慮如何第二遍 dfs 搞出其他點的 DP 值,可以發現 \(f_{x,j} = f_{i,j - 1}\) ,然後我們發現還有算重的部分,所以可以簡單容斥一下。

清北學堂 某題

傳送門

樹點染色類

Cell Phone Network G

傳送門
雙倍經驗

一開始的時候還以爲和黑白染色差不多的那種,但是仔細一想就會發現不對勁。

我們設 \(f_{x,0/1/2}\) 分別爲:

  • \(f_{x,0}\) 表示 i 被自己覆蓋 的最小花費
  • \(f_{x,1}\) 表示 i 被兒子覆蓋 的最小花費
  • \(f_{x,2}\) 表示 i 被父親覆蓋 的最小花費

可以發現,x 自己覆蓋的時候,也就是 x 自己建信號塔的時候,他的兒子們建或不建都可,所以轉移方程就是 \(f_{x,0} += \min \{ f_{son,0}, f_{son,1},f_{son,2}\}\)

可以發現,自己被兒子覆蓋的時候只需要被其中一個兒子覆蓋就可以了,所以轉移方程就是: \(f_{x.1} = f_{sx.0} + \sum \big(\min (f_{son,0}, f_{son,1})\big )\)

可以發現,他自己如果被父親覆蓋,說明他的兒子一定沒有被他自己覆蓋,所以狀態轉移方程就是: \(f_{x,2} += \min \{ f_{son. 0}, f_{son.1}\}\)

樹形揹包

狀壓DP

棋盤類

[SCOI2005]互不侵犯

洛谷
loj
簡化題意
讓你在一個棋盤上放置 k 個棋子,每個棋子的八個方位不能有其他的棋子。


\(f_{i, j, s}\) 表示,第 i 行放置的方式爲 j,放置了多少個棋子。
\(sit_x\) 表示 x 中 1 的個數,可以預處理,以及當前狀態下放置的國王的個數 gs 。

那麼 \(f_{i,j,s} = \sum f_{i-1, k, s - gs_j}\)

轉移的條件就是:

  • \(sit_j \ \& \ sit_k\) 及上下有重複的棋子。
  • \((sit_j << 1) \& sit_k\) 及左上右下有重複的棋子。
  • \(sit_j \& (sit_k << 1)\) 及左下右上有重複的棋子。

以上條件如果都不滿足,那麼可以轉移。

code
loj

炮兵陣地

洛谷
簡化題意
也可以看成放旗子,但是隻能把棋子放到空地上,而且棋子上下各兩個不能有其他棋子,左右各兩個不能有其他棋子。


\(f_{l,s,i}\) 表示上一行的狀態爲 l,當前這一行的狀態爲 s,現在是第 i 行時能放置的最多的炮兵數,那麼轉移方程可以很容易的推出,\(sum_s\) 表示當前的狀態 s 中包含多少個 1。

\(f_{l,s,i} = max(f_{l,s,i}, f[fl,l,i-1]+sum_s)\)

下邊只需要判斷那裏能放那裏不能放就行了。

可以用一個數組 \(a_i\) 來表示第 i 行中,山的位置是 1,平地爲 0。

判斷當前狀態有沒有在平原上可以直接 \(s \ \& \ a_i\) 就行了。
code
洛谷

Corn Fields G

洛谷
簡化題意
其實和上邊的問題都差不多,只是 prework 有些不懂而已。

** code **
洛谷

蒙德里安的夢想

AcWing
題意分析
rua,可以分割棋盤,但是我們發現分割棋盤之後會有好多長方形被攔腰折斷,所以可以沒被折斷的看做 0,折斷的看做 1,那麼下一行中那些折斷的就必須看做 0, 剩下的可以是1 也可以是 0。


\(f_{i,j}\) 表示第 i 行的形態 爲 j 時,前 i 行的分割方案的總數。

\(i-1\) 的形態 k 轉移到第 i 行的 j 形態,當且僅當:

  • j 和 k 的 \(\&\) 結果是 0, 這保證了每個數字 1 下方必須是數字 0 ,代表繼續補全上比方豎着的長方形。
  • j 和 k 的 \(\big|\) 每一段連續的 0 必須有偶數個,代表了橫着放的長方形。

我們可以 DP 求出 \([0, 2^M - 1]\) 內所有滿足“二進制表示下每一段都是連續的0有偶數個”的整數,記錄下來。

不難發現

\[\displaystyle f_{i.j} = \sum_{j \ \& \ k= 0 且 j \big| k \in s} f_{i-1, k} \]

code
AcWing

奇怪的狀壓

[AHOI2009]中國象棋

洛谷
簡化題意
在一個 \(n \times m\) 的棋盤上最多可以放置多少個炮,試任意的兩個炮不能相互打到。


感覺這也不像是一個狀壓DP,但是標籤上就是這麼寫的..

可以發現,讓任意的兩個炮不能相互打到就是,任意的一行一列都不能有兩個炮。

\(f_{i,j,k}\) 表示前 i 行中 j 列有一個炮,k 列有兩個炮的最大方案數。

可以發現,可以由一下狀態轉移而來。

  • \(f_{i-1,j, k}\),可以一個都不放,那就是上一個的方案數。
  • \(f_{i-1, j-1,k}\),從一個炮都沒有放的列轉移而來,因爲有 \(m - (j - 1) - k\) 個沒有放任何炮的列,所以最後要乘上這個係數。
  • \(f_{i-1,j+1,k-1}\),這個表示從 j+1 個放了一個炮的,在這些列中任意放上一個,讓放一個的變成放兩個的,因爲那些列可以隨便取,所以要乘以 \(j+1\)
  • \(f_{i-1,j - 2, k}\), 當前這一行放兩個,都放到沒有放過的地方,這個時候沒有放過的地方有 \(m - (j - 2) - k\) 個,從這些中任取兩個,就是 \({m-(j - 2) - k}\choose 2\),乘上這個係數。
  • \(f_{i - 1, j, k-1}\),當前這一行放兩個,放到同一列中,讓那一列變成兩個炮,因爲當前沒放過的有 \(m - j -(k -1)\)
  • \(f_{i-1,j+2, k - 2}\),從原本有一個炮的列中任意選兩個,各放上一個炮,\({j+2} \choose 2\),乘上這個係數。

code
洛谷

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章