DP笔记

DP

区间dp

例题 朋友

\(n\) 个房间,\(m\) 个人。第 \(i\) 个人能进入第 \([li, ri]\) 个房间。若最终一个房间里有 \(x\) 个人,则这个房间会有 \(x\) 的快乐值。问最大的快乐值是多少。

\(n \le 300\)

思路

可以证明,最优的答案存在一个房间,使得这个房间的人最多,且能到这个房间的人全部来了。

因为对于任何一个最大值,有个人没来,那么这个人到这里来之后一定会使答案增大。

所以设 f_{i,j} 表示只考虑只能到区间 \([i, j]\) 里面的人,最大的快乐值是什么。

枚举最大值在 \(k\),那么 \(f_{i,j} = max f_{i,k−1} + f_{k+1,j} + v^2\),其中 \(v\) 表示这里面能到 \(k\) 的人。

那么直接求 \(v\) 不太好求,那么考虑不能到 \(k\) 的人,他们的区间
必须在 \([i, k − 1]\)\([k + 1, j]\) 里面,减掉就行。
时间复杂度 \(O(n^3)\)

AGC033D

定义一个矩阵的凌乱度是:

  • 如果这个矩阵字符全部相等,那么是 \(0\)
  • 否则将这个矩阵切一刀,得到两个矩阵的凌乱度 \((a, b)\)

对所有\(max(a, b) + 1\) 求最小值就是这个矩阵的凌乱度。

现在给定一个 \(n × m\) 的矩阵,求它的凌乱度。

\(n, m \le 185\)

思路

最暴力的 dp:设 \(f_{i,j,k,l}\) 表示左上角 \((i, j)\),右下角 \((k, l)\) 的矩阵的凌乱度,那么转移直接枚举切了哪一刀就行。

时间复杂度 \(O(n^5)\),优化可以做到 \(O(n^4)\),很难通过。

但是可以注意到,\(f_{i,j,k,l}\) 会很小:具体地,若区间长或宽不等于\(1\),则可以将它对半切开,这样的答案即是 \(log n + log m\)
所以上面 \(f_{i,j,k,l}\) 的状态有很多其实是浪费(重复)的:因为它们的 dp 值一样。

而且很显然的一件事情是:\(i, j, k\) 不变,随着 \(l\) 增大,\(f_{i,j,k,l}\)不会变小,所以可以设:\(g_{t,i,j,k}\) 表示最大的 \(l\),使得 \(f_{i,j,k,l} \le t\)

这样状态数就是 \(O(n^3 log n)\) 了。

考虑转移:如果是横着切的,那么肯定贪心地让上面下面都尽可能多,那么上面肯定切到 \(p = g_{t−1,i,j,k}\),下面就会切到

\(g_{t−1,i,p+1,k}\),那么横着切的最小凌乱度就是这个。

那么如果是竖着切,则枚举切在了 \(x\) 右边,则\(g_{t,i,j,k} = max min(g_{t−1,i,j,x}, g_{t−1,x+1,j,k})\),这样仍然无法通过。

可以发现,随着 \(x\) 增大,\(g_{t−1,i,j,x}\) 增大,\(g_{t−1,x+1,j,k}\) 减小,所以我们只需要找到最大的 \(x\),使得

$g_{t−1,i,j,x} < g_{t−1,x+1,j,k} $然后算出切在 \(x\) 右边和 \(x + 1\) 右边哪个优就行。

那如何找到 \(x\) 呢?仍然是利用单调性:当 \(i, j\) 不变时,若 \(k\) 变成 \(k + 14\),则刚刚找到的 \(x\)\(g_{t−1,i,j,x}\) 不变,\(g_{t−1,x+1,j,k}\) 可能变大。所以当\(k\)变大时,\(x\) 也会跟着变大。所以双指针,枚举 \(k\) 的同时
维护 \(x\) 即可。
时间复杂度 \(O(n^3log n)\)

数位DP

P9387

给定 \(n, m\),设 $ x = 1 ⊕ 2 ⊕ · · · ⊕ n − 1 ⊕ n ⊕ m$,求有多少组 \((a, b, c)\)

满足 \(a + b + c ≤ n 或 a + b + c = m\),且\((a + b + c) ⊕ a ⊕ c = x, b > 0\),对 \(109 + 7\) 取模。(如果 \(a + b + c\)既比 \(n\) 小也等于 \(m\),那么需要算两次)

\(n, m ≤ 1018\),多组数据,每组数据要求复杂度 \(O(log n)\)

思路

先不管 \(a + b + c = m\),因为会了 \(a + b + c ≤ n\) 自然就能得到\(a + b + c = m\) 的方案数。

可以从低到高进行 dp,设 \(f_{i,j,0/1,0/1}\) 表示从低到高 dp 完前$ i$ 位,

向第 \(i + 1\) 位进位的个数使 \(j,b\) 是否大于 $0,a + b + 第 \(i\)

是否大于 \(n\)。转移暴力枚举 \(a, b, c\) 的第i 位是什么就行,最后答案是 \(f_{60,0,1,0}\)

单组时间复杂度 \(O(log n)\)

决策单调性

P3515

给定长为 \(n\) 的序列 \(a\),对每个 \(i\),找到最小的非负整数 \(p_i\),满足
对于 \(1 ≤ j ≤ n\),有 \(a_j ≤ a_i + p_i − \sqrt {|i − j|}\)
\(n ≤ 5 × 105\)

思路

忽略非负整数的限制,显然 \(p_i = \max\limits_{i\le j\le n}a_j+\sqrt{|i − j|}-a_i\)

先假设 \(j < i\),之后再倒着做一遍即可。

这个也是有决策单调性的,即若 \(j_1 < j_2\)\(j_2\)\(i\) 处比 \(i\) 更大,

那么 \(j_1\) 就没有用了(证明即考虑 \(i − j_2 < i − j_1\),那么 \(\sqrt{i-j_2}\)增速快于\(\sqrt{i-j_1}\)

所以也可以像刚刚那样做,算分界点的话手解一下方程即可。时间复杂度 \(O(n)\)

那么这种方法,如果 \(x\) 没有一个确定的表达式,而是若干题目所给的变量来决定的呢?

比如 \(f_i = \max_{g_j} + w(j, i)\),这个 \(w(j, i)\) 是要靠读入的变量来算的,那么这个时候可以通过二分找到 \(x\)

时间复杂度 \(O(n log n)\)

但还有另外一种简单、好写的 \(O(n log n)\) 的方法:分治。

\(solve(l, r, L, R)\) 表示现在要找 \(f_l\)\(f_r\) 的值,且它们的最优决策点在 \(L\)\(R\) 之间。

每次可以找到\(mid =\frac{l+r}{2}\) ,然后遍历 \(L\)\(R\) 并找到最优决策点\(y\),然后递归到 \(solve(l, mid - 1, L, y)\)

\(solve(mid + 1, r, y, R)\)

这样分治树总共 \(log n\) 层,且每层 \(L, R\) 的范围都是后面一个接着前面一个的,所以复杂度 \(O(n log n)\)

没写完,但就这样吧。

posted @ 2023-07-28 20:23  Kylin_ZHH  阅读(59)  评论(0)    收藏  举报