动态规划-区间DP
模板题一般长这样:
给一个区间[1,n]
给定一种操作:合并子区间,两个相邻子区间[i,j],[j+1,k]合并,得到一定的收益或者付出一些代价.这个收益或者代价必然是和两个子区间相关的函数,比如sum[i,j]+sum[j+1,k]
求将整个区间合并为一个数,获得的最大收益或者付出的最小代价
P1880 合并环状石子
一圈石子有n组,其中第i组有a[i]个,合并两组石子的代价是两组石子个数和,求将所有石子合并成一组需要的最大代价和最小代价
本题需要先把环拆成线,其弱化版也就是无环版本P1775 石子合并(弱化版
首先需要把这个圈解开,比较好的方法是将[1,n]展开成线性,然后复制一遍接到后面,就是
总下标 | 1 | 2 | 3 | ... | n-1 | n | n+1 | n+2 | n+3 | ... | 2n-1 | 2n |
---|---|---|---|---|---|---|---|---|---|---|---|---|
原下标 | 1 | 2 | 3 | ... | n-1 | n | 1 | 2 | 3 | ... | n-1 | n |
石子个数 | a[1] | a[2] | a[3] | .. | a[n-1] | a[n] | a[1] | a[2] | a[3] | ... | a[n-1] | a[n] |
设计状态
\[ max\_dp[i][j]=将[i,j]区间合并成一组的最大代价\\ min\_dp[i][j]=将[i,j]区间合并 成一组的最小代价 \]
显然边界条件是: \[ \forall _{1\le i\le 2n}max\_dp[i][i]=0\\ \forall _{1\le i\le 2n}min\_dp[i][i]=0\\ \] 意思是将一组石子和自己合并(合并个寂寞),其代价为0,因为不需要合并
\[ max\_dp[i][j]=\max_{i\leq k<j}(max\_dp[i][k]+max\_dp[k+1][j])+\sum_{i\leq l\leq j}a[l]\\ min\_dp[i][j]=\max_{i\leq k<j}(min\_dp[i][k]+min\_dp[k+1][j])+\sum_{i\leq l\leq j}a[l] \]
其中\(\sum_{i\leq l\leq j}a[l]\)也就是从i到j的石子总个数,可以维护前缀和降低复杂度 \[ \sum_{i\leq l\leq j}a[l]=prefix[j]-prefix[i-1] \]
完整代码
1 |
|
P1063 合并能量珠子
每个珠子有两头,记作(l,r),相邻两个珠子的相向两头数值相同,比如 \[ (a,b)(b,c)(c,d)(d,a) \] 合并两个相邻珠子\((a,b)(b,c)\)得到的能量是\(a\times b\times c\),合并之后形成新珠子\((a,c)\)
将整个项链合并,求得到的最大能量值
还是将整个环拆成[1,n]然后拷贝一份接到后面
\(dp[i][j]\)表示合并区间[i,j]能够得到的最大能量,其中i表示第i个珠子,j表示第j个珠子
边界条件仍然是\(dp[i][i]=0\),只有一个珠子不需要合并
状态转移: \[ dp[i][j]=\max_{i\le k< j}(dp[i][k]+dp[k+1][j]+node[k].l\times node[k].r\times node[k+1].r) \]
1 |
|
P1005 矩阵取数
对于一个给定的 \(n \times m\) 的矩阵,矩阵中的每个元素 \(a_{i,j}\) 均为非负整数。游戏规则如下:
- 每次取数时须从每行各取走一个元素,共 \(n\) 个。经过 \(m\) 次后取完矩阵内所有元素;
- 每次取走的各个元素只能是该元素所在行的行首或行尾;
- 每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元素值 \(\times 2^i\),其中 \(i\) 表示第 \(i\) 次取数(从 \(1\) 开始编号);
- 游戏结束总得分为 \(m\) 次取数得分之和。
对于任意矩阵,可以求出取数后的最大得分。
由于第i次取数的权重为\(2^i\),显然应尽量保留大数放到最后取
并且取第一行和第二行的数没有影响
因此实际上可以先解决一行内的取数问题,问题转化为:
m个数,第i个数是a[i],每次取数只能从头或者尾取数,第j次取数x的得分是\(2^j\times x\),求m次取数最高得分
如果没有只能头尾取数的限制,显然直接m个数排序后按照从小到达的顺序取数,现在有一个只能从头或者尾取数的限制
暴力搜索的算法容易想到dfs(deque,level)表示当前双端队列deque,当前要取第level个数
但是m最大为80,这就意味着dfs的时间复杂度是\(O(2^{80})\)显然超时
显然要使用更吊的方法
设计状态: \[ dp[i][j]\leftarrow要在闭区间[i,j]中取数,全部取完后最终能够得到的最大值 \]
状态转移: \[ dp[i][j]=max(dp[i][j-1]+a[j][j]\times 2^k,dp[i+1][j]+a[i][i]\times 2^k) \] 其中k是第k次取数,k=n-i-j+2
dp[1][n]
是第1次取数
屑题害得考尼玛的高精度,有个🔨意思啊,没活可以咬打火机
P4767 邮局
v个村庄,p个邮局
第i个村庄在a[i]位置
邮局都要建在村庄里,要求所有村庄到最近邮局的距离和最小
求这个最小距离和是多少
设计状态: \[ dp[i][j]表示前i个村庄放了j个邮局,最小距离和 \]
状态转移 \[ dp[i][j]=\min_{1\le k\le i}(dp[k][j-1]+w(k+1,i)) \] 其中\(w(l,r)\)表示,在第l个和第r个(包括两头)村庄之间放一个邮局时,这一段上的村庄的最短距离和
这个玩意儿怎么求呢?
设\([l,r]\)区间上的村庄坐标为\(x[l],x[l+1],x[l+2],...,x[r-1],x[r]\)
假设放这个邮局的坐标为\(x\),则问题相当于 \[ w(l,r)=min(|x-x[l]|+|x-x[l+1]|+|x-x[l+2]|+...+|x-x[r-1]|+|x-x[r]|) \] 令\(f(x)=|x-x[l]|+|x-x[l+1]|+|x-x[l+2]|+...+|x-x[r-1]|+|x-x[r]|\)
也就是求该函数在\([l,r]\)上的最小值点
怎么求呢?画图观察一下,
如果有奇数项,比如\(y=|x-x_1|+|x-x_2|+|x-x_3|\),这个函数必然有一个尖儿,也就是我们需要的最小值,并且这个极小值点等于 \(中位数(x_1,x_2,x_3)\)
如果有偶数项,比如\(y=|x-x_1|+|x-x_2|+|x-x_3|+|x-x_4|\),就没有这个尖儿,但是有一个水平的最低谷,这个最低谷介于两个中位数之间
这就有\(w(l,r)\)的求解方法了:
1 | w(l,r): |
每次调用\(w(l,r)\),时间复杂度都是\(T(r-l+1)=O(n)\)
可以预处理所有的\(w(l,r)\)
1 | for(int l=1;l<=n;++l){ |
外面又套了\(O(n^2)\)的循环
总复杂度是\(O(n^3)\),然而n最大是3000,显然\(O(n^3)\)会超时
如果能给他降为\(O(n^2)\)就可以了
这个\(w[l,r]\)的求法可以继续优化
假设已经知道了\(w[l,r-1]\),现在要求\(w[l,r]\) \[ w[l,r]=w[l,r-1]+x[r]-x[mid(l,r)]\\ mid(l,r)=\lfloor\frac{l+r}{2}\rfloor \] 这条结论看上去很诡异,太简洁了,并且好像中位数的变动只会影响到新加入的第r个村庄,前面的[l,r-1]村庄到邮局的最近距离和仍然是使用的原来的中位数,我自己推导的式子中是有中位数的变化的
如果\(r-l+1\)是一个奇数,那么最右边再加一个,下标中位数不变,相当于邮局位置不用改动,那么[l,r]所有的村庄到这个邮局的距离和不变,\(w[l,r+1]=w[l,r]+|x[r+1]-x[mid]|\)
如果\(r-l+1\)是一个偶数,那么最右边再加一个,下标中位数得往右移动一个,新mid=老mid+1,那么
\(w[l,r+1]=w[l,r]+|x[新mid]-x[老mid]|\times (l-r+1)+|x[r+1]-x[新mid]|\)
再考虑\(r-l+1\)是奇数的情况,如果保持下标中位数不变,实际上对于[l,r+1]时,这个中位数是左中位数
完全可以+1使用右中位数
因此两种情况统一了,即 \[ w[l,r+1]=w[l,r]+(x[mid+1]-x[mid])\times (r-l+1)+x[r+1]-x[mid+1] \]
然而仔细一想确实可以忽略中位数的变化.怎么想呢?
\[ \begin{aligned} w[l,r-1]&=\sum_{l\le k\le r-1}|x[k]-x[mid(l,r-1)]|\\ w[l,r]&=\sum_{l\le k\le r}|x[k]-x[mid(l,r)]|\\ \end{aligned} \] 如果[l,r-1]之间有偶数个村庄,那么选取邮局建立在左中位数村庄或者右中位数村庄,甚至两者直接的地方(虽然只允许建立在村庄里),[l,r-1]所有的村庄到这个邮局的距离和都是最短的,这在图上对应水平谷底那一段
不妨让邮局建立在右中位数村庄,也就是\(\lfloor\frac{l+r-1}{2}\rfloor+1\)
那么[l,r]有奇数个村庄,其中位数唯一,也就是\(\lfloor \frac{l+r}{2}\rfloor\)
由于[l,r-1]之间有偶数个村庄,因此可以设\(r-1-l+1=r-l=2k\)带入上两式 \[ \begin{aligned} \lfloor\frac{l+r-1}{2}\rfloor+1&=\lfloor k+l-\frac{1}{2}\rfloor+1=k+l-1+1=k+l\\ \lfloor \frac{l+r}{2}\rfloor&=\lfloor k+l\rfloor=k+l \end{aligned} \] 这就证明了[l,r]的中位数恰好是[l,r-1]的右中位数,因此两种情况可以选择同一个中位数,因此新加入r村庄的时候w[l,r]直接加上w[l,r-1],无需进行中位数的校正
如果[l,r-1]之间有奇数个村庄,那么只需要让[l,r]选取左中位数和[l,r-1]的中位数是一个点,同理可证
证毕
实际上证完了看这个结论很容易想,但是之前思维就太僵化了,以后记住这个结论吧
加上w计算的优化,加上四边形不懂事的优化
okk,现在dp的时间复杂度成功降到了\(O(n^2)\)
优化后的w,其计算的时间复杂度也是\(O(n^2)\)
总时间复杂度就是\(O(n^2)\)
应该可以通过了吧
不想写了,开摆,明天写了题解然后再看看四边形不懂事优化
阳了四天,摆了四天...
首先是不加四边形不等式优化的代码
1 |
|
动态规划-四边形不等式优化
证明及其他详细资料见luoyongjun999/code: code (github.com)
区间dp的状态转移,一般长这样(以P1775石子合并为例):
1 | for(int delta=1;delta<=n;++delta){ |
三层循环,复杂度\(O(n^3)\)
使用四边形不等式优化(能够证明成立的前提下),可以将最里圈k的遍历范围从\([i,j)\),缩减到\([s[i][j-1],s[i+1][j]\),这个范围结合上i和j的范围能够证明,三层循环的时间复杂度实际上是\(O(n^2)\)
这里\(s[i][j]\)的实际意义是,当\(dp[i][j]=\min_{i\leq k<j}(dp[i][k]+dp[k+1][j])+\sum_{i\leq k\leq j}a[k]\)在k处取得最小值时,此时的k就作为\(s[i][j]\)
边界条件是\(s[i][i]=i\)
用法见P1775
P1775 石子合并(弱化版)
一排N堆石头第i堆石头有a[i]个,合并两堆石头的得分是两堆石头的数量和
求最后合并只剩一堆时取得的最小得分,其中\(N\le 300,a[i]\leq 1000\)
P1880那个题是环状的,这个是线性的
设计状态: \[ dp[i][j]表示合并[i,j]所有的石子,获得的最小得分 \] 边界条件: \[ dp[i][i]=0,即合并一堆时合并个寂寞 \] 状态转移: \[ dp[i][j]=\min_{i\leq k<j}(dp[i][k]+dp[k+1][j])+\sum_{i\leq k\leq j}a[k] \] 前缀和优化: \[ \sum_{i\leq k\leq j}a[k]=prefix[j]-prefix[i-1]\\ dp[i][j]=\min_{i\leq k<j}(dp[i][k]+dp[k+1][j])+prefix[j]-prefix[i-1] \] 四边形不等式优化:
实际上需要证明\(dp[i][j]\),但是可以证明只要是\(w[i][j]\)满足四边形不等式,\(dp[i][j]\)也自动满足四边形不等式,
因此首先证明\(w[i][j]=\sum_{i\leq k\leq j}a[k]=prefix[j]-prefix[i-1]\)满足应用四边形不等式的条件,即证:
1.定义:对于任意\(i<i+1\leq j<j+1\),都有 \[ w[i][j]+w[i+1][j+1]\leq w[i][j+1]+w[i+1][j] \] 2.单调性:对于任意\(i\le i'\le j\le j'\),都有 \[ w[i][j']\ge w[i'][j] \] 证明如下:
对于定义: \[ w[i][j]+w[i+1][j+1]=prefix[j]-prefix[i-1]+prefix[j+1]-prefix[i] \]
\[ w[i][j+1]+w[i+1][j]=prefix[j+1]-prefix[i-1]+prefix[j]-prefix[i] \]
左=右,定义成立
对于单调性: \[ w[i][j']-w[i'][j]=prefix[j']-prefix[i-1]-prefix[j]+prefix[i'-1]\\ =(prefix[j']-prefix[j])+(prefix[i'-1]-preifx[i-1]) \] 由于\(i\le i'\le j\le j'\),并且前缀函数\(prefix[i]\)单调不减,因此上式非负,即 \[ w[i][j']\ge w[i'][j] \] 证毕
下面就可以使用四边形不等式的性质定理优化了
\[ dp[i][j]=\min_{s[i-1][j]\leq k<s[i][j+1]}(dp[i][k]+dp[k+1][j])+prefix[j]-prefix[i-1] \] 这里\(s[i][j]\)的实际意义是,当\(dp[i][j]=\min_{i\leq k<j}(dp[i][k]+dp[k+1][j])+\sum_{i\leq k\leq j}a[k]\)在k处取得最小值时,此时的k就作为\(s[i][j]\)
边界条件是\(s[i][i]=i\)
\(s[i][j]\)的计算并不是头铁地从i到j遍历k先求\(s[i][j]\),而是跟随dp一起求
1 |
|
至于四边形不等式的正确性,现在还没有完全转阴,啥时候脑子不浑了再看吧...