因为昨天在Codeforces上设计的区间dp错了(错过了上紫的机会),觉得很难受。看看学长好像也有学,就不用看别的神犇的了。


区间dp处理环的时候可以把序列延长一倍。

下面是 $O(n^3)$ 的朴素区间dp:

for(int len = ; len<=n; len++) { //枚举长度
for(int i = ; i+len<=n+; i++) { //枚举起点
int j = i+len - ;
for(int k = i; k<j; k++) { //枚举分割点,更新小区间最优解
dp[i][j] = min(dp[i][j],dp[i][k]+dp[k+][j]+cost[i][j]);
}
}
}

下面是四边形优化的 $O(n^2)$ 区间dp:

首先,使用四边形优化要满足下面的性质:

1.区间包含的单调性:

当小区间包含在大区间中,则小区间的成本不高于大区间的成本

2.四边形不等式:交叉小于包含

对于 $a<b≤c<d$ ,若 $f[a][c]+f[b][d]≤f[a][d]+f[c][d]$ ,则称 $f$ 满足四边形不等式。

定理:若能证明 $cost$ 满足1和2,则 $dp$ 也满足2。

定理:记 $s[i][j]$ 为 $dp[i][j]$ 取得最值时的分割点的下标 $k$ ,若 $dp$ 满足2,则 $s[i][j]$ 单调,也就是 $s[i][j]≤s[i][j+1]≤s[i+1][j+1]$ 。

应用上述结论:

$dp[i][j]=min\{dp[i][k]+dp[k+1][j]\}+cost[i][j],s[i][j-1]≤k≤s[i+1][j]$

我们减少了k的枚举量,而k的枚举量为 $O(n^2)$。

而上述定理的证明……先省略吧……那我们只需要证明cost满足1和2,就可以使用四边形优化了。

另外要注意

s[i][i]=i;
for(int len = ; len<=n; len++) { //枚举长度
for(int i = ; i+len<=n+; i++) { //枚举起点
int j = i+len - ;
dp[i][j]=INF;
for(int k = s[i][j-]; k<=s[i+][j]; k++) { //枚举分割点,更新小区间最优解
if(dp[i][k]+dp[k+][j]<dp[i][j]){
dp[i][j]=dp[i][k]+dp[k+][j];
s[i][j]=k;
}
}
dp[i][j]+=cost[i][j];
}
}

事先计算出 $cost[i][j]$ 就可以了。


来,开始看看学长搞了什么。

BZOJ 1260 涂色paint

https://www.lydsy.com/JudgeOnline/problem.php?id=1260

看了学长说的,设 $dp[i][j]$ 为把 $[i,j]$ 涂成指定颜色需要的最少cost,那么转移的时候怎么搞呢?


洛谷 P1880 石子合并

https://www.luogu.org/problemnew/show/P1880

#include<bits/stdc++.h>
using namespace std;
#define ll long long int n;
int a[];
int prefix[]; int dp[][]; inline int sum(int l,int r) {
return prefix[r]-prefix[l-];
} int main() {
while(~scanf("%d",&n)) {
memset(dp,0x3f,sizeof(dp));
for(int i=; i<=n; i++) {
scanf("%d",&a[i]);
prefix[i]=prefix[i-]+a[i];
dp[i][i]=;
} for(int i=n+; i<=*n; i++) {
a[i]=a[i-n];
prefix[i]=prefix[i-]+a[i];
dp[i][i]=;
} for(int len = ; len<=n; len++) { //枚举长度
for(int i = ; i+len-<=*n; i++) { //枚举起点
int j = i+len - ;
for(int k = i; k<j; k++) { //枚举分割点,更新小区间最优解
dp[i][j] = min(dp[i][j],dp[i][k]+dp[k+][j]+sum(i,j));
//printf("k=%d\n",k);
}
//printf("sum(i,j)=%d dp[%d][%d]=%d\n",sum(i,j),i,j,dp[i][j]);
}
} int ans=0x3f3f3f3f;
for(int i=;i<=n;i++){
ans=min(ans,dp[i][i+n-]);
}
printf("%d\n",ans); memset(dp,,sizeof(dp));
for(int len = ; len<=n; len++) { //枚举长度
for(int i = ; i+len-<=*n; i++) { //枚举起点
int j = i+len - ;
for(int k = i; k<j; k++) { //枚举分割点,更新小区间最优解
dp[i][j] = max(dp[i][j],dp[i][k]+dp[k+][j]+sum(i,j));
}
}
} ans=;
for(int i=;i<=n;i++){
ans=max(ans,dp[i][i+n-]);
}
printf("%d\n",ans); }
}

水题,记得要扩大一倍。

使用四边形不等式时,运算必须是最小值,最大值不满足单调性,如下。

#include<bits/stdc++.h>
using namespace std;
#define ll long long int n;
int a[];
int prefix[]; int dp[][];
int dp2[][];
int s[][]; inline int sum(int l,int r) {
return prefix[r]-prefix[l-];
} int main() {
while(~scanf("%d",&n)) {
memset(dp,0x3f,sizeof(dp));
memset(dp2,0x3f,sizeof(dp2)); for(int i=; i<=n; i++) {
scanf("%d",&a[i]);
prefix[i]=prefix[i-]+a[i];
dp[i][i]=;
dp2[i][i]=;
s[i][i]=i;
} for(int i=n+; i<=*n; i++) {
a[i]=a[i-n];
prefix[i]=prefix[i-]+a[i];
dp[i][i]=;
dp2[i][i]=;
s[i][i]=i;
} for(int len = ; len<=n; len++) { //枚举长度
for(int i = ; i+len<=*n+; i++) { //枚举起点
int j = i+len - ;
for(int k = s[i][j-]; k<=s[i+][j]; k++) { //枚举分割点,更新小区间最优解
if(dp[i][k]+dp[k+][j]<dp[i][j]){
dp[i][j]=dp[i][k]+dp[k+][j];
s[i][j]=k;
}
//printf("k=%d\n",k);
} dp[i][j]+=sum(i,j);
//printf("dp[%d][%d]=%d\n",i,j,dp[i][j]);
}
} for(int len = ; len<=n; len++) { //枚举长度
for(int i = ; i+len-<=*n; i++) { //枚举起点
int j = i+len - ;
for(int k = i; k<j; k++) { //枚举分割点,更新小区间最优解
dp2[i][j] = min(dp2[i][j],dp2[i][k]+dp2[k+][j]+sum(i,j));
//printf("k=%d\n",k);
}
if(dp[i][j]!=dp2[i][j])
printf("sum(i,j)=%d dp[%d][%d]=%d dp2[%d][%d]=%d\n",sum(i,j),i,j,dp[i][j],i,j,dp2[i][j]);
}
} int ans=0x3f3f3f3f;
for(int i=;i<=n;i++){
ans=min(ans,dp[i][i+n-]);
}
printf("%d\n",ans); for(int i=; i<=*n; i++) {
s[i][i]=i;
} memset(dp,,sizeof(dp));
memset(dp2,,sizeof(dp2)); for(int len = ; len<=n; len++) { //枚举长度
for(int i = ; i+len<=*n+; i++) { //枚举起点
int j = i+len - ;
for(int k = s[i][j-]; k<=s[i+][j]; k++) { //枚举分割点,更新小区间最优解
if(dp[i][k]+dp[k+][j]>dp[i][j]){
dp[i][j]=dp[i][k]+dp[k+][j];
s[i][j]=k;
}
}
dp[i][j]+=sum(i,j);
}
} for(int len = ; len<=n; len++) { //枚举长度
for(int i = ; i+len-<=*n; i++) { //枚举起点
int j = i+len - ;
for(int k = i; k<j; k++) { //枚举分割点,更新小区间最优解
dp2[i][j] = max(dp2[i][j],dp2[i][k]+dp2[k+][j]+sum(i,j));
//printf("k=%d\n",k);
}
if(i<&&j<&&dp[i][j]!=dp2[i][j])
printf("sum(i,j)=%d dp[%d][%d]=%d dp2[%d][%d]=%d\n",sum(i,j),i,j,dp[i][j],i,j,dp2[i][j]);
}
} ans=;
for(int i=;i<=n;i++){
ans=max(ans,dp[i][i+n-]);
}
printf("%d\n",ans); }
}

模板 - 动态规划 - 区间dp的更多相关文章

  1. 动态规划——区间dp

    在利用动态规划解决的一些实际问题当中,一类是基于区间上进行的,总的来说,这种区间dp是属于线性dp的一种.但是我们为了更好的分类,这里仍将其单独拿出进行分析讨论. 让我们结合一个题目开始对区间dp的探 ...

  2. 动态规划——区间DP,计数类DP,数位统计DP

    本博客部分内容参考:<算法竞赛进阶指南> 一.区间DP 划重点: 以前所学过的线性DP一般从初始状态开始,沿着阶段的扩张向某个方向递推,直至计算出目标状态. 区间DP也属于线性DP的一种, ...

  3. 动态规划---区间dp

    今天写内网题,连着写了两道区间dp,这里就总结一下. 区间dp思想主要是先枚举f[i][j]中的i,再枚举j,再枚举一个1~j之间的变量k,一般是f[i][j] = max(f[i][j],f[i][ ...

  4. [hdu contest 2019-07-29] Azshara's deep sea 计算几何 动态规划 区间dp 凸包 graham扫描法

    今天hdu的比赛的第一题,凸包+区间dp. 给出n个点m个圆,n<400,m<100,要求找出凸包然后给凸包上的点连线,连线的两个点不能(在凸包上)相邻,连线不能与圆相交或相切,连线不能相 ...

  5. 模板 - 动态规划 - 数位dp

    #include<bits/stdc++.h> using namespace std; #define ll long long ]; ll dp[][/*可能需要的状态2*/];//不 ...

  6. Hdu OJ 5115 Dire Wolf (2014ACM/ICPC亚洲区北京站) (动态规划-区间dp)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5115 题目大意:前面有n头狼并列排成一排, 每一头狼都有两个属性--基础攻击力和buff加成, 每一头 ...

  7. Light OJ 1025 - The Specials Menu(动态规划-区间dp)

    题目链接:http://www.lightoj.com/volume_showproblem.php?problem=1025 题目大意:一串字符, 通过删除其中一些字符, 能够使这串字符变成回文串. ...

  8. 【模板】区间dp

    有N堆石子排成一排,每堆石子有一定的数量.现要将N堆石子合并为1堆.在合并的过程中只能每次将相邻的两堆石子合并,每次合并的花费为这两堆石子之和,求合并成1堆的最小花费. dp[i][j]表示将区间[i ...

  9. [SCOI2007]压缩(动态规划,区间dp,字符串哈希)

    [SCOI2007]压缩 状态:设\(dp[i][j]\)表示前i个字符,最后一个\(M\)放置在\(j\)位置之后的最短字串长度. 转移有三类,用刷表法来实现. 第一种是直接往压缩串后面填字符,这样 ...

随机推荐

  1. 原生Base64编码/解码(OC与Swift)

    Objective-C NSString *plainString = @"foo"; Encoding NSData *plainData = [plainString data ...

  2. Spring事务超时时间可能存在的错误认识

    摘自:http://jinnianshilongnian.iteye.com/blog/1986023, 感谢作者. 1.先看代码 1.1.spring-config.xml <bean id= ...

  3. Java多线程系列 基础篇09 Object.wait/notifyJVM源码实现

    转载 https://www.jianshu.com/p/f4454164c017 作者 占小狼 最简单的东西,往往包含了最复杂的实现,因为需要为上层的存在提供一个稳定的基础,Object作为java ...

  4. php 获取上上个月数据 使用 strtotime('-1 months')的一个bug

    今天,使用php 日期函数处理数据,发现一个问题. 具体场景是这样的,我一直以为strtotime  格式化当前日期 或 指定日期可以找到对应的数据,比如我要查找上上个与的数据,因为我要获取当前时间的 ...

  5. 同程联盟景点门票动态程序 beta1.0源码

    经过一段时间的开发,以及内部测试,同程网联盟景区新版程序正式发布推出,感谢广大联盟会员一直以来的支持与关注! 同程网联盟景区新版程序新功能介绍: 1.统一的页面风格.页面风格将与随后推出的度假线路.酒 ...

  6. BZOJ 2021 [Usaco2010 Jan]Cheese Towers:dp + 贪心

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2021 题意: John要建一个奶酪塔,高度最大为m. 他有n种奶酪.第i种高度为h[i]( ...

  7. python练习1(简单爬虫)

    做一个简单的练习 目标:爬取中文小说 目标网站:http://www.biqule.com/book_58/26986.html 只爬取正文部分. 使用requests库来获取网页信息,使用re库正则 ...

  8. python基础-正则2

    正则函数 Python提供re模块,包含所有正则表达式的功能 由于python的字符串本身也有\转义,所以需要注意: s = "ABC\\-001" 对应的正则表达式应为:'ABC ...

  9. AtCoder Grand Contest #026 C - String Coloring

    Time Limit: 3 sec / Memory Limit: 1024 MB Score : 600600 points Problem Statement You are given a st ...

  10. hdu3501Calculation 2——欧拉函数模板

    题目: Problem Description Given a positive integer N, your task is to calculate the sum of the positiv ...