先来看一道题目:

有 \(n\) 堆石子排成一行,第 \(i\) 堆有 \(a_i\) 个石子每次选择相邻的两堆石子,将其合并为一堆,记录该次合并的得分为两堆石子个数之和。已知每堆石子的石子个数,求当所有石子合并为一堆时,最小的总得分。

区间DP模板题。设 \(dp(i,j)\) 为区间 \([i,j]\) 的最小得分,则状态转移方程为:

\[dp(i,j) = \min_{k=i}^{j-1}\{dp(i,k)+dp(k+1,j)+\sum_{s=i}^j a_s\}
\]

code:

#include<bits/stdc++.h>
using namespace std;
const int inf=1<<30;
int dp[1005][1005],a[1005],sum[1005]; //sum[]数组为a[]数组的前缀和,这样就能快速求出∑a_s(i<=s<=j)。
int main()
{
int n,i,j,k,s;
scanf("%d",&n);
for(i=1;i<=n;i++) dp[i][i]=0;
sum[0]=0;
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum[i]=sum[i-1]+a[i];
}
for(k=1;k<n;k++)
{
for(i=1;i<=n-k;i++)
{
j=i+k;
dp[i][j]=inf;
for(s=i;s<j;s++)
dp[i][j]=min(dp[i][j],dp[i][s]+dp[s+1][j]+sum[j]-sum[i-1]);
}
}
printf("%d\n",dp[1][n]);
return 0;
}

容易看出,上面的代码时间复杂度为 \(O(n^3)\),数据一旦大一点,就会导致 TLE。

怎么办呢?我们来看代码的循环部分:

for(k=1;k<n;k++)
{
for(i=1;i<=n-k;i++)
{
j=i+k;
dp[i][j]=inf;
for(s=i;s<j;s++)
dp[i][j]=min(dp[i][j],dp[i][s]+dp[s+1][j]+sum[j]-sum[i-1]);
}
}

前两层循环枚举距离和起点,无法优化,但是第三层循环寻找断点是可以优化的。怎么做呢?

可以再开一个 \(s\) 数组来记录每个区间的最优断点,然后变量 \(k\) (寻找断点)每次只从 \(s(i,j-1)\) 循环到 \(s(i+1,j)\),这样时间复杂度可以从 \(O(n^3)\) 降到近似 \(O(n^2)\)。

如何证明这样的循环来找断点是对的呢?

见下:(Edited by charliezhi2007

DP的平行四边形不等式证明,m[i][j]即为dp[i][j],s[i][j]表示i~j的最优化断点。

设a , b , c , d(a<=b<=c<=d)    //结论是两边之和大于第三边(四边形)
m(a,c) + m(b,d) <= m(a,d) + m(b,c) //四边形对边相等
s[ i ][j-1] <= s[ i ][ j ] <= s[i+1][ j ] s[ i ][ j ]<=s[i+1][ j ]思考
d=s[ i ][ j ] ,i < i + 1 <= k < d k<-[ i , j ] //设d为断点位置,假设k小于d,k为i,j中任意断点
mk(i,j) = m(i,k) + m(k + 1,j) + sum(i,j) //表示以k为断点i,j合并的代价 sum是i到j所有值得和
md(i,j) = m(i,d) + m(d + 1,j) + sum(i,j) //表示以d为断点i,j合并的代价(代价最小)
mk(i,j) >= md(i,j) > 0 //d为断点将i,j合并的代价最小因为d是最优断点
=> mk(i,j) - md(i,j) > 0 (mk(i + 1,j) - md(i + 1,j)) - (mk(i,j) - md(i,j)) //判断k>d 或 d>k因为上面假设k<d要证明
=(mk(i + 1,j) + md(i,j)) - (md(i + 1,j) + mk(i,j)) //将系数为负数的项,系数为正数的项放在一起
= (m(i + 1,k) + m(k + 1,j) + m(i,d) + m(d + 1,j) + sum(i,j) + sum(i + 1,j))
- (m(i + 1,d) + m(k + 1,j) + m(i,k) + m(k + 1,j)+ sum(i,j) + sum(i + 1,j)) //将式子展开
=>将减号两边的m(k + 1,j) 和 m(d + 1,j) 和 sum(i,j) 和 sum(i + 1,j)相互消元
=(m(i + 1,k) + m(i,d)) - (m(i + 1,d) + m(i,k))
=> i<i + 1<k <d = a <b <c <d //两式变量相等
=>(m(i + 1,k) + m(i,d)) - (m(i + 1,d) + m(i,k)) = (m(b,c) + m(a,d)) - (m(b,d) + m(a,c)) >0 //因ad+bc>=ac+bd所以该式大于0
=>(mk(i + 1,j) - md(i + 1,j)) - (mk(i,j) - md(i,j)) >0 //则这个也大于0
因d<=b 则b=s[i + 1][ j ] 下面求s[ i ][j-1]的思路于上面一致,则最终得出k∈[s[ i ][j-1] , s[i-1][ j ]]。

通过上述推论,可得出 \(s(i,j-1)\leq s(i,j)\leq s(i+1,j)\),再经过一波分析,可得出 \(s(i+1,j)-s(i,j-1)\) 小到几乎可以忽略不计,所以上述程序的复杂度降到了 \(O(n^2)\)。

献上丑陋AC代码:

#include<bits/stdc++.h>
using namespace std;
const int inf=1<<30;
int dp[1005][1005],a[1005],sum[1005],s[1005][1005];
int main()
{
int n,i,j,k,ss;
scanf("%d",&n);
for(i=1;i<=n;i++)
{
dp[i][i]=0;
s[i][i]=i;
}
sum[0]=0;
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum[i]=sum[i-1]+a[i];
}
for(k=1;k<n;k++)
{
for(i=1;i<=n-k;i++)
{
j=i+k;
dp[i][j]=inf;
for(ss=s[i][j-1];ss<=s[i+1][j];ss++)
{
if(dp[i][ss]+dp[ss+1][j]+sum[j]-sum[i-1]<dp[i][j])
{
dp[i][j]=dp[i][ss]+dp[ss+1][j]+sum[j]-sum[i-1];
s[i][j]=ss;
}
}
}
}
printf("%d\n",dp[1][n]);
return 0;
}

dp的平行四边形优化的更多相关文章

  1. HDU3480_区间DP平行四边形优化

    HDU3480_区间DP平行四边形优化 做到现在能一眼看出来是区间DP的问题了 也能够知道dp[i][j]表示前  i  个节点被分为  j  个区间所取得的最优值的情况 cost[i][j]表示从i ...

  2. 蓝桥杯:合并石子(区间DP+平行四边形优化)

    http://lx.lanqiao.cn/problem.page?gpid=T414 题意:…… 思路:很普通的区间DP,但是因为n<=1000,所以O(n^3)只能拿90分.上网查了下了解了 ...

  3. 51 nod 石子归并 + v2 + v3(区间dp,区间dp+平行四边形优化,GarsiaWachs算法)

    题意:就是求石子归并. 题解:当范围在100左右是可以之间简单的区间dp,如果范围在1000左右就要考虑用平行四边形优化. 就是多加一个p[i][j]表示在i到j内的取最优解的位置k,注意能使用平行四 ...

  4. [poj3017] Cut the Sequence (DP + 单调队列优化 + 平衡树优化)

    DP + 单调队列优化 + 平衡树 好题 Description Given an integer sequence { an } of length N, you are to cut the se ...

  5. dp的斜率优化

    对于刷题量我觉得肯定是刷的越多越好(当然这是对时间有很多的人来说. 但是在我看来我的确适合刷题较多的那一类人,应为我对知识的应用能力并不强.这两天学习的内容是dp的斜率优化.当然我是不太会的. 这个博 ...

  6. HDU 2829 区间DP & 前缀和优化 & 四边形不等式优化

    HDU 2829 区间DP & 前缀和优化 & 四边形不等式优化 n个节点n-1条线性边,炸掉M条边也就是分为m+1个区间 问你各个区间的总策略值最少的炸法 就题目本身而言,中规中矩的 ...

  7. DP的四边形优化

    DP的四边形优化 一.进行四边形优化需要满足的条件 1.状态转移方程如下: m(i,j)表示对应i,j情况下的最优值. w(i,j)表示从i到j的代价. 例如在合并石子中: m(i,j)表示从第i堆石 ...

  8. CSP 201612-4 压缩编码 【区间DP+四边形不等式优化】

    问题描述 试题编号: 201612-4 试题名称: 压缩编码 时间限制: 3.0s 内存限制: 256.0MB 问题描述: 问题描述 给定一段文字,已知单词a1, a2, …, an出现的频率分别t1 ...

  9. Codevs 3002 石子归并 3(DP四边形不等式优化)

    3002 石子归并 3 时间限制: 1 s 空间限制: 256000 KB 题目等级 : 钻石 Diamond 题目描述 Description 有n堆石子排成一列,每堆石子有一个重量w[i], 每次 ...

随机推荐

  1. 出现 java.lang.OutOfMemoryError: PermGen space 错误的原因及解决方法

    一.原因及解决方法[1] 1.原因:堆内存的永久保存去区内存分配不足(缺省默认为64M),导致内存溢出错误. 2.解决方法:重新分配内存大小,-Xms1024M -Xmx2048M -XX:PermS ...

  2. Vue/小程序/小程序云+Node+Mongo开发微信授权、支付和分享

    大家好,我是河畔一角,今天给大家介绍我的第三门实战课程:基于微信开发的H5.小程序和小程序云的授权.支付和分享专项课程. 一.这一次为什么会选择微信支付和分享的课题呢? 金庸的小说中曾提到:有人的地方 ...

  3. OO助教的退休感想

    深夜失眠+刚赶完火车的胡言乱语,切莫当真,择日修改 一年前,我在学姐的怂恿鼓励下,加上了吴老师的微信,表达了想担任下学期的OO助教的想法.三天后,我到新主楼参加OO助教的面试,其实还是蛮紧张的,毕竟自 ...

  4. sql server数据库备份单个表的结构和数据生成脚本【转】

    1.使用场景:sql server数据库备份单个表的结构和数据,在我们要修改正式系统的数据的一天或者多条某些数据时候,要执行update语句操作,安全稳健考虑,最好先做好所修改的表的结构和数据备份! ...

  5. tp的增删改查的结果判断?

    参考: https://blog.csdn.net/qq_27930635/article/details/78853908 总之, 要用 全等 来判断, = = = 注意, 不要再用 mysql_a ...

  6. t5_sumdoc.txt

    C:\Users\Administrator\Documents\sumdoc 2019\sumdoc t5 final\sumdoc t511C:\Users\Administrator\Docum ...

  7. MQTT研究之EMQ:【EMQX使用中的一些问题记录(4)】

    最近比较忙,有些关于EMQ的使用问题,没有时间记录了,趁这个周末抽点时间,将最近遇到的,觉得比较有价值的一个问题,分享给大家吧. 这里是针对前面的一篇博客,做的一个深入研究,关于订阅系统总线判断设备上 ...

  8. 【大数据】Spark On Yarn

    Spark在YARN中有yarn-cluster和yarn-client两种运行模式: I. Yarn client 在yarn-client模式下,Driver运行在Client上,通过Applic ...

  9. JS 生成随机字符串 随机颜色

    使用Math.random()生成随机数 0.7489584611780002数字的.toString(n) 将数字转换为 n 进制的字符串 n取值范围(0~36)"0.vbpjw8lipf ...

  10. PHP 字符串和十六进制互转

    今天在做项目中,因为要调用别人网站的接口,结果需要对请求和返回的时间进行十六进制加密处理,于是在网上查了下资料谢了一个转换Demo做个记录. 如果在TP下使用可以将下面函数放到common.php中 ...