模板 - 动态规划 - 区间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\)位置之后的最短字串长度. 转移有三类,用刷表法来实现. 第一种是直接往压缩串后面填字符,这样 ...
随机推荐
- (C)程序控制块(TCB)
程序控制块 1. 程序控制块 从代码上看,程序控制块就是一个结构体.例如: typedef struct tcb{ char * tast_name; //任务名字 int p; //任务重要级别 i ...
- JavaScript预解释是一种毫无节操的机制
前言 JavaScript是一门解释型的语言 , 想要运行JavaScript代码需要两个阶段 编译阶段: 编译阶段就是我们常说的JavaScript预解释(预处理)阶段,在这个阶段JavaScrip ...
- Codeforces Round #551 (Div. 2) A~E题解
突然发现上一场没有写,那就补补吧 本来这场应该5题的,结果一念之差E fail了 A. Serval and Bus 基本数学不解释,假如你没有+1 -1真的不好意思见人了 #include<c ...
- 无法远程连接阿里云的Mysql
问题描述:昨天使用阿里云安装了Mysql,无法远程连接,排除端口号错误.防火墙.Mysql权限问题后,最后发现是阿里云安全组规则限制问题: 解决方式: 1.访问阿里云控制台,实例-->管理 2. ...
- html5--1.19 通用属性
html5--1.19 通用属性 学习要点: 1.通用属性的概念及几个常用的通用属性2.对属性值的若干点补充 通用属性 通用属性(全局属性)可以用于任何的HTML5元素:通用属性有十几种:这节课不会全 ...
- JQuery调用iframe子页面函数/对象的方法
父页面有个ID为mainfrm的iframe,iframe连接b.html,该页面有个函数test 在父页面调用b.html的test方法为: $("#mainfrm")[0].c ...
- 【译】在ES6中如何优雅的使用Arguments和Parameters
原文地址:how-to-use-arguments-and-parameters-in-ecmascript-6 ES6是最新版本的ECMAScript标准,而且显著的改善了JS里的参数处理.我们现在 ...
- mysql之count
两种引擎对count的处理 CREATE TABLE `test` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `name` char(15) D ...
- [Shell]grep命令
我是好文章的搬运工,原文来自ChinaUnix,博主scq2099yt,地址:http://blog.chinaunix.net/uid-22312037-id-4217835.html 一.基本用法 ...
- [CTSC 2012] Cheat
[题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=2806 [算法] 首先建立广义后缀自动机 注意到问题具有单调性 , 不妨对于每组询问二 ...