dp優化 | 各種dp優化方式例題精選( 二 )


2.1 P1440 求m區間內的最小值2.1.1 題目大意給定一個長度為 \(n\) 的數列 \(a\),對于每個 \(i\) 輸出 \(min\{a_{i-m},a_{i-m+1},..,a_{i-1}\}\) 。
2.1.2 數據范圍\(1\leq m\leq n\leq 2\times10^6\),\(1\leq a_i\leq3\times10^7\) 。
2.1.3 做法好像和單調隊列優化dp沒什么關系?
此題用于體驗單調隊列,就不多寫了,直接用單調隊列模擬操作即可 。
const int N = 2000010;int n, m, s[N], l = 1, r, a[N];int main() { n = read(), m = read(); printf("0\n"); for (int i = 1; i <= n - 1; i++) {a[i] = read();while (r >= l && a[s[r]] > a[i]) r--;s[++r] = i;while (s[r] - s[l] + 1 > m && l <= r) l++;printf("%d\n", a[s[l]]); } return 0;}2.2 P5858 「SWTR-03」Golden Sword2.2.1 題目大意有 \(n\) 個物品,編號 \(1..n\),每個物品有堅固值 \(a_i\) 。
進行 \(n\) 次操作,對于每次操作,執行以下步驟:

  1. 取出不超過 \(s\) 個物品 。
  2. 放入物品 \(i\) 。
其中容器最多容納 \(w\) 個物品 。
每次操作會產生 \(a_i\times 物品數(包括放入的物品)\) 的貢獻 。
求 \(n\) 次操作后總貢獻的最大值 。
2.2.2 數據范圍\(1\leq s\leq w\leq n\leq5\times10^3\),\(|a_i|\leq10^9\) 。
2.2.3 做法設 \(f_{i,j}\) 表示正在執行第 \(i\) 次操作,容器內共有 \(j\) 個物品所能得到的最大貢獻值 。
那么 \(f_{i,j}=\max\{f_{i-1,k}+a_i\times j\}\) 。
其中 \(j-1\leq k\leq \min\{w,j-1+s\}\) 。
于是就得到了一個45分做法(long long沒開全只有35)
const int N = 5010;const ll INF = 1e18;int n, w, s;ll f[N][N], ans = -INF, a[N];int main() { n = read(), w = read(), s = read(); for (int i = 1; i <= n; i++) a[i] = read(); for (int i = 0; i <= n; i++)for (int j = 0; j <= w; j++)f[i][j] = -INF; f[0][0] = 0; for (int i = 1; i <= n; i++)for (int j = 1; j <= w; j++)for (int k = j - 1; k <= min(w, j - 1 + s); k++)f[i][j] = max(f[i][j], f[i - 1][k] + a[i] * j); for (int i = 0; i <= w; i++) ans = max(ans, f[n][i]); printf("%lld\n", ans); return 0;}(不如先動手寫個部分分做法?)
考慮優化 。先把式子變一下:\(f_{i,j}=\max\{f_{i-1,k}\}+a_i\times j\)\((j-1\leq k\leq \min\{w,j-1+s\})\) 。很顯然對吧,就是把原來max中重疊的部分提出來而已 。雖然說這么一提好像不能優化什么,你會發現 , \(\max\{f_{i-1,k}\}\) 好像可以用單調隊列優化?!
const int N = 5010;const ll INF = 1e18;int n, w, s;ll f[N][N], ans = -INF, a[N];int ss[N];int main() { n = read(), w = read(), s = read(); for (int i = 1; i <= n; i++) a[i] = read(); for (int i = 0; i <= n; i++)for (int j = 0; j <= w; j++)f[i][j] = -INF; f[0][0] = 0; for (int i = 1; i <= n; i++) {int l = 1, r = 0;ss[++r] = w;for (int j = w; j; j--) {while (f[i - 1][ss[r]] < f[i - 1][j - 1] && r >= l) r--;ss[++r] = j - 1;while ((ss[l] - ss[r] + 1) - 1 > s && l <= r) l++;f[i][j] = f[i - 1][ss[l]] + j * a[i];} } for (int i = 0; i <= w; i++) ans = max(ans, f[n][i]); printf("%lld\n", ans); return 0;}3. 斜率優化dpOI-Wiki 傳送門
3.1 P3195 [HNOI2008]玩具裝箱3.1.1 題目大意有 \(n\) 件物品,第 \(i\) 件物品壓縮后占用 \(C_i\) 的長度 。
現需把這些物品壓縮進一些容器里,制作一個容器的花費為 \((x-L)^2\),其中 \(x\) 表示容器長度 。
每個容器中的物品編號需要是連續的,而將編號 \(i\) 到 \(j\) 的所有物品放在一個容器中,占用的空間 \(x=j-i+\sum_{k=i}^j C_k\) 。
求壓縮完所有物品所需的總花費的最小值 。
3.1.2 數據范圍\(1\leq n\leq 5\times10^4\),\(1\leq L\leq10^7\),\(1\leq C_i\leq10^7\) 。
3.1.3 做法設 \(f_i\) 表示壓縮到第 \(i\) 件物品所需的最小花費,不難列出轉移方程:
\(f_i=\min\{f_j+(i-j-1+\sum_{k=j+1}^i c_k-L)^2\}\)
令 \(sum_i=\sum_{k=1}^i c_k\),原式可轉化為:
\(f_i=\min\{f_j+(i-j-1+sum_i-sum_j-L)^2\}\) 。
移項得:
\(f_i=\min\{f_j+((i+sum_i)-(j+sum_j)-(L+1))^2\}\)
令 \(pre_i=sum_i+i\),原式可轉化為:
\(f_i=\min\{f_j+(pre_i-pre_j-(L+1))^2\}\)
把式子展開再合并:
\(f_i=\min\{f_j+pre_i^2-pre_i\times pre_j-(L+1)\times pre_i-pre_i\times pre_j+pre_j^2+(L+1)\times pre_j-(L+1)\times pre_i+(L+1)\times pre_j+(L+1)^2\}\)
\(f_i=\min\{f_j+pre_i^2+pre_j^2-2\times pre_i\times pre_j-2\times(L+1)\times(pre_i-pre_j)+(L+1)^2\}\)
\(f_i=\min\{f_j+(pre_i-pre_j)^2-2\times(pre_i-pre_j)\times(L+1)+(L+1)^2\}\)
\(f_i=\min\{f_j+(pre_i-pre_j-(L+1))^2\}\)
\(f_i=\min\{f_j+((pre_i-(L+1))-pre_j)^2\}\)
\(f_i=\min\{f_j+(pre_i-(L+1))^2-2\times(pre_i-(L+1))\times pre_j+pre_j^2\}\)
\(f_i-(pre_i-(L+1))^2=\min\{f_j+pre_j^2-2\times(pre_i-(L+1))\times pre_j\}\)
令:
\(\begin{eqnarray}\begin{cases}b_i=f_i-(pre_i-(L+1)^2)\\x_j=pre_j\\y_j=f_j+pre_j^2\\k_i=2\times(pre_i-(L+1))\end{cases}\end{eqnarray}\)

推薦閱讀