模板 - 动态规划 - 区间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\)位置之后的最短字串长度. 转移有三类,用刷表法来实现. 第一种是直接往压缩串后面填字符,这样 ...
随机推荐
- apache 绿色版 安装
下载绿色版apache 本文已apache2.4为例 http://www.apachehaus.com/cgi-bin/download.plx 下载后解压 打开readme_first.html文 ...
- SVN支干合并(转载)
分支用来维护独立的开发支线,在一些阶段,你可能需要将分支上的修改合并到最新版本,或者将最新版本的修改合并到分支. 此操作十分重要,在团队开发中,如果你是SVN 的维护者此环节可以说是必不可少,因为团队 ...
- 深入理解JVM - Java内存区域与内存溢出异常 - 第二章
一 运行时数据区域 JVM在执行Java程序的过程中会把它管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以及创建和销毁的时间. 程序计数器 程序计数器(Program Counter ...
- 使用gdb调试c/c++代码
转自 http://blog.csdn.net/haoel/article/details/2879 GDB概述———— GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具.或许,各位比较 ...
- linkedin databus介绍——监听数据库变化,有新数据到来时通知其他消费者app,新数据存在内存里,多份快照
概要结构如下图. 图中显示:Search Index和Read Replicas等系统是Databus的消费者.当主OLTP数据库发生写操作时,连接其上的中继系统会将数据拉到中继中.签入在Search ...
- openfire性能测试
使用TSung对Jabber服务器openfire进行压力测试 http://blog.csdn.net/spider_zhcl/article/details/6073920 Tsung负载测试Ti ...
- form 提交数据编码梳理
之前对form单提交的操作一直都是迷迷糊糊,知道怎么用,但是随着ajax2的出现,我们有更多的方式操作form表单提交,但是底层的原理我们要好好的做个梳理. 常见的form提交有post和get这两种 ...
- codeforces 659C C. Tanya and Toys(水题+map)
题目链接: C. Tanya and Toys time limit per test 1 second memory limit per test 256 megabytes input stand ...
- codeforces 558C C. Amr and Chemistry(bfs)
题目链接: C. Amr and Chemistry time limit per test 1 second memory limit per test 256 megabytes input st ...
- MySQL_杭州11月1-29号在线产品在线天数、销售天数_20161129
杭州11月1-29号在线产品在线天数.销售天数 1.产品在这个时间段内的每一天的在线情况,然后聚合计算每个产品的在线天数,每一天的在线情况 如果在线记为1,不在线为null 2.计算每个产品在这个时间 ...