模板 - 动态规划 - 区间dp
因为昨天在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的更多相关文章
- 动态规划——区间dp
在利用动态规划解决的一些实际问题当中,一类是基于区间上进行的,总的来说,这种区间dp是属于线性dp的一种.但是我们为了更好的分类,这里仍将其单独拿出进行分析讨论. 让我们结合一个题目开始对区间dp的探 ...
- 动态规划——区间DP,计数类DP,数位统计DP
本博客部分内容参考:<算法竞赛进阶指南> 一.区间DP 划重点: 以前所学过的线性DP一般从初始状态开始,沿着阶段的扩张向某个方向递推,直至计算出目标状态. 区间DP也属于线性DP的一种, ...
- 动态规划---区间dp
今天写内网题,连着写了两道区间dp,这里就总结一下. 区间dp思想主要是先枚举f[i][j]中的i,再枚举j,再枚举一个1~j之间的变量k,一般是f[i][j] = max(f[i][j],f[i][ ...
- [hdu contest 2019-07-29] Azshara's deep sea 计算几何 动态规划 区间dp 凸包 graham扫描法
今天hdu的比赛的第一题,凸包+区间dp. 给出n个点m个圆,n<400,m<100,要求找出凸包然后给凸包上的点连线,连线的两个点不能(在凸包上)相邻,连线不能与圆相交或相切,连线不能相 ...
- 模板 - 动态规划 - 数位dp
#include<bits/stdc++.h> using namespace std; #define ll long long ]; ll dp[][/*可能需要的状态2*/];//不 ...
- Hdu OJ 5115 Dire Wolf (2014ACM/ICPC亚洲区北京站) (动态规划-区间dp)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5115 题目大意:前面有n头狼并列排成一排, 每一头狼都有两个属性--基础攻击力和buff加成, 每一头 ...
- Light OJ 1025 - The Specials Menu(动态规划-区间dp)
题目链接:http://www.lightoj.com/volume_showproblem.php?problem=1025 题目大意:一串字符, 通过删除其中一些字符, 能够使这串字符变成回文串. ...
- 【模板】区间dp
有N堆石子排成一排,每堆石子有一定的数量.现要将N堆石子合并为1堆.在合并的过程中只能每次将相邻的两堆石子合并,每次合并的花费为这两堆石子之和,求合并成1堆的最小花费. dp[i][j]表示将区间[i ...
- [SCOI2007]压缩(动态规划,区间dp,字符串哈希)
[SCOI2007]压缩 状态:设\(dp[i][j]\)表示前i个字符,最后一个\(M\)放置在\(j\)位置之后的最短字串长度. 转移有三类,用刷表法来实现. 第一种是直接往压缩串后面填字符,这样 ...
随机推荐
- Flask的Debug功能非常酷
Flask是一个Python开发框架.在试用的过程中发现它的debug功能非常cool.如下图所示,在出错的页面每条栈新的右边都有一个按钮,点击之后我们可以执行Python代码,而且非常重要的一点是通 ...
- 关于调用notifyDataSetChanged刷新PullToRefreshListView列表无反应解决办法
文章转载自:关于调用notifyDataSetChanged刷新PullToRefreshListView列表无反应解决办法 | TeachCourse
- X86/X64处理器体系结构及寻址模式
由8086/8088.x86.Pentium发展到core系列短短40多年间,处理器的时钟频率差点儿已接近极限.尽管如此,自从86年Intel推出386至今除了添加一些有关流媒体的指令如mmx/sse ...
- ceph pool 管理
创建池 [root@node1 ~]# ceph osd pool create monitor pool 'monitor' created 查看池 [root@node1 ~]# ceph osd ...
- python 搜集参数的共有项和所有项
搜集共性项和所有项 ###搜集共有参数值 def intersect(*args): res=[] for x in args[0]: for other in args[1:]: if x not ...
- LeetCode:访问所有节点的最短路径【847】
LeetCode:访问所有节点的最短路径[847] 题目描述 给出 graph 为有 N 个节点(编号为 0, 1, 2, ..., N-1)的无向连通图. graph.length = N,且只有节 ...
- Android Weekly Notes Issue #310 (Google IO特别篇)
Android Weekly Issue #310 May 20th, 2018 Android Weekly Issue #290 本期既有本次Google IO对于Play Console的更新简 ...
- 数据结构ADT是什么
抽象数据类型的缩写 abstract data type .表示数据结构的抽象模型.数据结构是一个数据概念的定义,通过各种工具对数据结构的概念类型的描述称之为抽象数据类型,简单地说是指一个数学模型以及 ...
- hdu1015 —— 回溯
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1015 因为之前看了下刘汝佳的<算法入门经典>,就用类似打印全排列,八皇后的方法做.本以为不 ...
- Spring Boot2.0之整合log4j
传统方式打印日志比较复杂, 每次打印需要定义全局变量 private static final Logger logger = LoggerFactory.getLogger(SjpControlle ...