因为昨天在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. Flask的Debug功能非常酷

    Flask是一个Python开发框架.在试用的过程中发现它的debug功能非常cool.如下图所示,在出错的页面每条栈新的右边都有一个按钮,点击之后我们可以执行Python代码,而且非常重要的一点是通 ...

  2. 关于调用notifyDataSetChanged刷新PullToRefreshListView列表无反应解决办法

    文章转载自:关于调用notifyDataSetChanged刷新PullToRefreshListView列表无反应解决办法 | TeachCourse

  3. X86/X64处理器体系结构及寻址模式

    由8086/8088.x86.Pentium发展到core系列短短40多年间,处理器的时钟频率差点儿已接近极限.尽管如此,自从86年Intel推出386至今除了添加一些有关流媒体的指令如mmx/sse ...

  4. ceph pool 管理

    创建池 [root@node1 ~]# ceph osd pool create monitor pool 'monitor' created 查看池 [root@node1 ~]# ceph osd ...

  5. python 搜集参数的共有项和所有项

    搜集共性项和所有项 ###搜集共有参数值 def intersect(*args): res=[] for x in args[0]: for other in args[1:]: if x not ...

  6. LeetCode:访问所有节点的最短路径【847】

    LeetCode:访问所有节点的最短路径[847] 题目描述 给出 graph 为有 N 个节点(编号为 0, 1, 2, ..., N-1)的无向连通图. graph.length = N,且只有节 ...

  7. Android Weekly Notes Issue #310 (Google IO特别篇)

    Android Weekly Issue #310 May 20th, 2018 Android Weekly Issue #290 本期既有本次Google IO对于Play Console的更新简 ...

  8. 数据结构ADT是什么

    抽象数据类型的缩写 abstract data type .表示数据结构的抽象模型.数据结构是一个数据概念的定义,通过各种工具对数据结构的概念类型的描述称之为抽象数据类型,简单地说是指一个数学模型以及 ...

  9. hdu1015 —— 回溯

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1015 因为之前看了下刘汝佳的<算法入门经典>,就用类似打印全排列,八皇后的方法做.本以为不 ...

  10. Spring Boot2.0之整合log4j

    传统方式打印日志比较复杂, 每次打印需要定义全局变量 private static final Logger logger = LoggerFactory.getLogger(SjpControlle ...