【笔记】区间DP
记录一些基础的区间 \(\text{DP}\) 题。
0x00 AT_dp_n N - Slimes
最板的区间 \(\text{DP}\) 。
记 \(f[i][j]\) 表示合并 \(i\sim j\) 区间的最小代价,初始化为 \(\text{inf}\),\(f[i][i]=0\) (一个数不需要代价)。
第一维从小到大枚举区间长度 \(len\),第二维枚举左端点 \(i\),同时可以计算出右端点 \(j=i+len-1\),第三维枚举段点 \(k\),转移方程为
\]
其中 \(s\) 为前缀和。
AC code
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false)
#define TIE cin.tie(0),cout.tie(0)
#define mod 998244353
#define int long long
using namespace std;
int a[405],n,m,f[405][405],s[405];
signed main(){
IOS;TIE;
cin>>n;
memset(f,0x3f,sizeof(f));
for(int i=1;i<=n;i++) cin>>a[i],f[i][i]=0,s[i]=s[i-1]+a[i];
for(int len=2;len<=n;len++){
for(int i=1;i+len-1<=n;i++){
int j=i+len-1;
for(int k=i;k<j;k++){
f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]);
}
}
}
cout<<f[1][n]<<endl;
return 0;
}
双倍经验:P1775 石子合并(弱化版)
0x01 P1880 [NOI1995] 石子合并
来看常规版,也就是环上问题。
首先显然断环成链,把石子复制到两倍长,然后同时维护区间最大最小代价即可。最小要初始化为 \(\text{inf}\)。
注意区间长度最大仍取到 \(n\),但右端点最大能取到 \(2\times n\),最后答案就是 \(f[1,n]\sim f[n-1,n\times 2-1]\) 的最大 \(\text{or}\) 最小值。
AC Code
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false)
#define TIE cin.tie(0),cout.tie(0)
#define int long long
#define mod 998244353
using namespace std;
int n,a[205],s[205],fmx[205][205],fmn[205][205];
int ansmx,ansmn=1e9;
signed main(){
IOS;TIE;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i],a[n+i]=a[i];
for(int i=1;i<=n*2;i++) s[i]=s[i-1]+a[i];
for(int len=2;len<=n;len++){
for(int i=1;i+len-1<=n*2;i++){
int j=i+len-1;
fmn[i][j]=1e9;
for(int k=i;k<j;k++){
fmx[i][j]=max(fmx[i][j],fmx[i][k]+fmx[k+1][j]+s[j]-s[i-1]);
fmn[i][j]=min(fmn[i][j],fmn[i][k]+fmn[k+1][j]+s[j]-s[i-1]);
}
}
}
for(int i=1;i<n;i++){
ansmx=max(ansmx,fmx[i][i+n-1]);
ansmn=min(ansmn,fmn[i][i+n-1]);
}
cout<<ansmn<<endl<<ansmx<<endl;
return 0;
}
0x02 P3146 [USACO16OPEN]248 G
同样是区间合并,记 \(f[i][j]\) 为 \(i\sim j\) 区间可以合并出的数(一定要刚好合并成一个数),转移为
\]
其中判断条件意为两个相邻区间可以合成的数相同,但是不能为 \(0\)。
不同的是答案不为 \(f[1][n]\),因为整个序列最后不一定能合成一个数,每一次合时都要更新一下答案。
AC Code
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false)
#define TIE cin.tie(0),cout.tie(0)
#define int long long
#define mod 998244353
using namespace std;
int n,a[255],f[255][255],ans;
signed main(){
IOS;TIE;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i],ans=max(ans,a[i]);
f[i][i]=a[i];
}
for(int len=2;len<=n;len++){
for(int i=1;i+len-1<=n;i++){
int j=i+len-1;
for(int k=i;k<j;k++){
if(f[i][k]==f[k+1][j]&&f[i][k]){
f[i][j]=max(f[i][j],f[i][k]+1);
ans=max(ans,f[i][j]);
}
}
}
}
cout<<ans<<endl;
return 0;
}
0x03 P3147 [USACO16OPEN]262144 P
与上一题唯一的区别是数据范围,显然不能支持 \(n^3\) 算法了。
巧妙的思路:设 \(f[i][j]\) 表示左端点为 \(j\),能合出 \(i\) 这个数字的区间的右端点位置 \(+1\),其转移方程为
\]
初始化为对于每个输入的 \(a\),\(f[a][i]=i+1\)。
借助一个样例理解:2 2 3
。假设我们已经知道 \(f[3][1]=3\),因为以 \(1\) 为左端点,能合出 \(3\) 的区间的右端点 \(+1\) 为 \(3\),也就是区间 \([1,2]\),这时我们要算 \(f[4][1]\),\(f[4][1]=f[4-1][f[3][1]]=f[3][3]\),在读入 \(3\) 时 \(f[3][3]=4\),所以就成功合并了。
所以第一维枚举 \(i\),看一下数据范围发现最多只能合到 \(58\),第二维枚举可以转移来的区间的左端点 \(j\) 就好。
AC Code
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false)
#define TIE cin.tie(0),cout.tie(0)
#define int long long
#define mod 998244353
using namespace std;
int n,a,f[65][270005],ans;
signed main(){
IOS;TIE;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a;
f[a][i]=i+1;
}
for(int i=2;i<=60;i++){
for(int j=1;j<=n;j++){
if(!f[i][j]) f[i][j]=f[i-1][f[i-1][j]];
if(f[i][j]) ans=i;
}
}
cout<<ans<<endl;
return 0;
}
0x04 P4170 [CQOI2007]涂色
首先得知连续相同的一段颜色可以看做一个点。然后要分类讨论一下:
如果当前区间的两端点颜色相等,则转移只需要分别减掉左右端点取 \(\min\),转移方程为
\]
否则就枚举断点,转移方程为
\]
AC Code
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false)
#define TIE cin.tie(0),cout.tie(0)
#define int long long
#define mod 998244353
using namespace std;
int n,x,a[55],f[55][55],lst=-1;
string s;
signed main(){
IOS;TIE;
cin>>s;
for(int i=0;i<s.size();i++){
x=s[i]-'A';
if(x!=lst) a[++n]=x,lst=x;
}
memset(f,0x3f,sizeof(f));
for(int i=1;i<=n;i++) f[i][i]=1;
for(int len=2;len<=n;len++){
for(int i=1;i+len-1<=n;i++){
int j=i+len-1;
if(a[i]==a[j]) f[i][j]=min(f[i+1][j],f[i][j-1]);
else{
for(int k=i;k<j;k++){
f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]);
}
}
}
}
cout<<f[1][n]<<endl;
return 0;
}
0x05 P2858 [USACO06FEB]Treats for the Cows G/S
与之前的区间合并不太一样,这次只需把一个点合入一段区间即可,所以不用枚举断点的一维。
但是每个点都有要乘一个权值。考虑倒着做。单点最后取的倍率为 \(n\),所以初值 \(f[i][i]=a[i]\times n\)
设当前已取区间的长度为 \(len\),则取数的倍率为 \(n-len+1\) 。考虑从左边取还是从右边取,转移方程就是
\]
AC Code
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false)
#define TIE cin.tie(0),cout.tie(0)
#define int long long
#define mod 998244353
using namespace std;
int n,a[2005],f[2005][2005];
signed main(){
IOS;TIE;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i],f[i][i]=n*a[i];
for(int len=2;len<=n;len++){
for(int i=1;i+len-1<=n;i++){
int j=i+len-1;
f[i][j]=max(f[i][j-1]+a[j]*(n-len+1),f[i+1][j]+a[i]*(n-len+1));
}
}
cout<<f[1][n]<<endl;
return 0;
}
【笔记】区间DP的更多相关文章
- [学习笔记]区间dp
区间 \(dp\) 1.[HAOI2008]玩具取名 \(f[l][r][W/I/N/G]\) 表示区间 \([l,r]\) 中能否压缩成 \(W/I/N/G\) \(Code\ Below:\) # ...
- 算法笔记--区间dp
1.石子归并问题 dp[i][j]表示区间i到j合并所需的最小花费. 先求出小区间的最小花费,再转移到大的区间. 转移方程:dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1] ...
- 区间dp笔记√
区间DP是一类在区间上进行dp的最优问题,一般是根据问题设出一个表示状态的dp,可以是二维的也可以是三维的,一般情况下为二维. 然后将问题划分成两个子问题,也就是一段区间分成左右两个区间,然后将左右两 ...
- 区间dp学习笔记
怎么办,膜你赛要挂惨了,下午我还在学区间\(dp\)! 不管怎么样,计划不能打乱\(4\)不\(4\).. 区间dp 模板 为啥我一开始就先弄模板呢?因为这东西看模板就能看懂... for(int i ...
- 区间DP 学习笔记
前言:本人是个DP蒟蒻,一直以来都特别害怕DP,终于鼓起勇气做了几道DP题,发现也没想象中的那么难?(又要被DP大神吊打了呜呜呜. ----------------------- 首先,区间DP是什么 ...
- CH5301 石子合并【区间dp】
5301 石子合并 0x50「动态规划」例题 描述 设有N堆沙子排成一排,其编号为1,2,3,…,N(N<=300).每堆沙子有一定的数量,可以用一个整数来描述,现在要将这N堆沙子合并成为一堆, ...
- 【BZOJ-4380】Myjnie 区间DP
4380: [POI2015]Myjnie Time Limit: 40 Sec Memory Limit: 256 MBSec Special JudgeSubmit: 162 Solved: ...
- 【POJ-1390】Blocks 区间DP
Blocks Time Limit: 5000MS Memory Limit: 65536K Total Submissions: 5252 Accepted: 2165 Descriptio ...
- 区间DP LightOJ 1422 Halloween Costumes
http://lightoj.com/volume_showproblem.php?problem=1422 做的第一道区间DP的题目,试水. 参考解题报告: http://www.cnblogs.c ...
- BZOJ1055: [HAOI2008]玩具取名[区间DP]
1055: [HAOI2008]玩具取名 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 1588 Solved: 925[Submit][Statu ...
随机推荐
- 编写 bzt 脚本的正确姿势
这是今年1月24日的旧文,发现没在这里发过,就搬运过来了. 声明 本文讨论的使用场景主要为使用已有的 jmx 脚本,并配合 json 对 jmx 脚本进行部分参数的动态修改. 只补充一些官方文档上没有 ...
- 题解 P2471 【[SCOI2007]降雨量】
原题传送门 前置芝士 离散化 ST表和RMQ问题 二分 正文 首先我们来分析一下题意. 题目会给出两个大小为 \(n\) 的数组,\(y\) 和 \(r\) ,其中 \(y_i\) 表示第 \(i\) ...
- Grafana 入门知识介绍
通过[Configuration]>[Plugins]添加插件 通过[Configuration]>[Data Sources]添加数据源(分析对象) 通过[Server Admin]&g ...
- Prometheus 通过 consul 分布式集群实现自动服务发现
转载自:https://cloud.tencent.com/developer/article/1611091 1.Consul 介绍 Consul 是基于 GO 语言开发的开源工具,主要面向分布式, ...
- PTA 乙级解题笔记 1001 害死人不偿命的(3n+1)猜想
卡拉兹(Callatz)猜想: 对任何一个正整数 n,如果它是偶数,那么把它砍掉一半:如果它是奇数,那么把 (3n+1) 砍掉一半.这样一直反复砍下去,最后一定在某一步得到 n=1.卡拉兹在 1950 ...
- 对循环神经网络参数的理解|LSTM RNN Input_size Batch Sequence
在很多博客和知乎中我看到了许多对于pytorch框架中RNN接口的一些解析,但都较为浅显甚至出现一些不准确的理解,在这里我想阐述下我对于pytorch中RNN接口的参数的理解. 我们经常看到的RNN网 ...
- [CG从零开始] 5. 搞清 MVP 矩阵理论 + 实践
在 4 中成功绘制了三角形以后,下面我们来加载一个 fbx 文件,然后构建 MVP 变换(model-view-projection).简单介绍一下: 从我们拿到模型(主要是网格信息)文件开始,模型网 ...
- 关于csh-C-shell的记录
csh,由柏克莱大学的 Bill Joy 设计的,语法有点类似C语言,所以才得名为 C shell ,简称为 csh Bill Joy 是一个风云人物,他创立了 BSD 操作系统,开发了 vi 编辑器 ...
- 洛谷P4630 [APIO2018] Duathlon 铁人两项 (圆方树)
圆方树大致理解:将每个点双看做一个新建的点(方点),该点双内的所有点(圆点)都向新建的点连边,最后形成一棵树,可以给点赋予点权,用以解决相关路径问题. 在本题中,方点点权赋值为该点双的大小,因为两个点 ...
- 洛谷P2517 HAOI2010 订货 (费用流)
标准的费用流问题,关键在于巧妙地建模 一共有n个月份,源点设为0,汇点设为n+1 1.源点向所有月份连边,容量为正无穷,费用为该月进货的费用 2.每个月向下一个月连边,容量为仓库容量,费用为存货费用 ...