区间DP 学习笔记
前言:本人是个DP蒟蒻,一直以来都特别害怕DP,终于鼓起勇气做了几道DP题,发现也没想象中的那么难?(又要被DP大神吊打了呜呜呜。
-----------------------
首先,区间DP是什么?
区间DP是一种以区间长度为阶段的DP方法。这种DP的解法较为固定,一般都是先枚举区间长度,再枚举左端点,根据左端点+长度推出右端点,然后枚举中间的断点进行转移。
伪代码:
for (int len=;len<=n;len++)
for (int i=;i<=n-len+;i++)
{
int j=i+len-;
for (int k=i;k<j;k++) f[i][j]=max(f[i][j],f[i][k]+f[k+][j]);
ans=max(ans,f[i][j]);
}
一句题外话:最短路算法中的佛洛依德算法的本质就是区间DP。
--------------------------
区间DP有两种形式(还是需要选手自己转化的。
一.环型DP
1.石子合并
经典题目,每个OI初学者必做的一道题。
首先我们要解决的是环的问题。我们可以将长度扩大到原来的二倍,破换成链。这是一种非常重要的思想,以后做题会经常遇到。
然后我们考虑区间DP的问题。每个区间都是由子区间合并而来,代价是两个子区间之和。所以我们不妨枚举区间内的断点,看哪种合并方式能得到最优解。
所以不难得出状态转移方程:
$f1[i][j]=min(f1[i][j],f1[i][k]+f1[k+1][j])$
$f2[i][j]=max(f2[i][j],f2[i][k]+f2[k+1][j])$
初始化即为$f[i][i]=a[i]$。
Code:
#include<bits/stdc++.h>
using namespace std;
int f1[][],f2[][],s[][];
int a[],sum[],n,ans1,ans2;
void init()
{
cin>>n;
for (int i=;i<=n;i++){
cin>>a[i];
a[i+n]=a[i];
}
for (int i=;i<=n*;i++)
{
sum[i]=sum[i-]+a[i];
f2[i][i]=;f1[i][i]=;
}
}
void dp()
{
for (int l=;l<=n;l++)
for (int i=;i<=*n-l+;i++)
{
int j=i+l-;
f1[i][j]=0x7fffffff/;f2[i][j]=;
for (int k=i;k<j;k++)
{
f1[i][j]=min(f1[i][j],f1[i][k]+f1[k+][j]);
f2[i][j]=max(f2[i][j],f2[i][k]+f2[k+][j]);
}
f1[i][j]+=sum[j]-sum[i-];
f2[i][j]+=sum[j]-sum[i-];
}
ans1=0x7fffffff/;ans2=;
for (int i=;i<=n;i++) ans1=min(ans1,f1[i][i+n-]);
for (int i=;i<=n;i++) ans2=max(ans2,f2[i][i+n-]);
}
int main()
{
init();
dp();
cout<<ans1<<endl<<ans2<<endl;
return ;
}
这也是一道环型DP,而且细节蛮多的,有兴趣不妨可以到我的博客里看一看。链接已备好。
二.链型DP
有些题太过于直白导致一眼看出状态转移方程,这里就不写了。直接上一道比较有难度的题。
根据题中的提示,我们发现区间$[i,j]$的转移有两种情况:
1.直接顺着走下来。
2.走到某处折返。
又因为老张只能关他相邻的灯,所以我们得出状态转移方程:
$f[i][j][0]=min(f[i+1][j][0]+(pos[i+1]-pos[i])*(sum[n]-sum[j]+sum[i]),f[i+1][j][1]+(pos[j]-pos[i])*(sum[n]-sum[j]+sum[i]))$
$f[i][j][1]=min(f[i][j-1][1]+(pos[j]-pos[j-1])*(sum[i-1]+sum[n]-sum[j-1]),f[i][j-1][0]+(pos[j]-pos[i])*(sum[i-1]+sum[n]-sum[j-1]))$
其中前缀和要预处理,$0$表示在左端点,$1$表示在右端点。
Code:
#include<bits/stdc++.h>
using namespace std;
const int maxn=;
int f[maxn][maxn][],n,c,pos[maxn],w[maxn],sum[maxn];
int main()
{
scanf("%d%d",&n,&c);
for (int i=;i<=n;i++)
for (int j=;j<=n;j++) f[i][j][]=f[i][j][]=0x3f3f3f3f;
for (int i=;i<=n;i++) scanf("%d%d",&pos[i],&w[i]),sum[i]=w[i]+sum[i-];
f[c][c][]=f[c][c][]=;
for (int len=;len<=n;len++)
for (int i=;i<=n-len+;i++)
{
int j=i+len-;
f[i][j][]=min(f[i+][j][]+(pos[i+]-pos[i])*(sum[n]-sum[j]+sum[i]),f[i+][j][]+(pos[j]-pos[i])*(sum[n]-sum[j]+sum[i]));
f[i][j][]=min(f[i][j-][]+(pos[j]-pos[j-])*(sum[i-]+sum[n]-sum[j-]),f[i][j-][]+(pos[j]-pos[i])*(sum[i-]+sum[n]-sum[j-]));
}
printf("%d",min(f[][n][],f[][n][]));
return ;
}
后记:其实DP题目量还是比较大的,而且NOIp必考,所以要花大功夫在这上面。
区间DP 学习笔记的更多相关文章
- 区间dp学习笔记
怎么办,膜你赛要挂惨了,下午我还在学区间\(dp\)! 不管怎么样,计划不能打乱\(4\)不\(4\).. 区间dp 模板 为啥我一开始就先弄模板呢?因为这东西看模板就能看懂... for(int i ...
- 数位DP学习笔记
数位DP学习笔记 什么是数位DP? 数位DP比较经典的题目是在数字Li和Ri之间求有多少个满足X性质的数,显然对于所有的题目都可以这样得到一些暴力的分数 我们称之为朴素算法: for(int i=l_ ...
- DP学习笔记
DP学习笔记 可是记下来有什么用呢?我又不会 笨蛋你以后就会了 完全背包问题 先理解初始的DP方程: void solve() { for(int i=0;i<;i++) for(int j=0 ...
- 树形DP 学习笔记
树形DP学习笔记 ps: 本文内容与蓝书一致 树的重心 概念: 一颗树中的一个节点其最大子树的节点树最小 解法:对与每个节点求他儿子的\(size\) ,上方子树的节点个数为\(n-size_u\) ...
- 斜率优化DP学习笔记
先摆上学习的文章: orzzz:斜率优化dp学习 Accept:斜率优化DP 感谢dalao们的讲解,还是十分清晰的 斜率优化$DP$的本质是,通过转移的一些性质,避免枚举地得到最优转移 经典题:HD ...
- 区间DP学习总结
这段时间学习了区间DP,所以试着把学到的东西稍作总结,以备不时之需. 学习区间DP首先要弄清区间DP是为了解决什么问题:一般的DP主要是特征是一次往往只操作一个数值或者存储可以不连续的物品的状态(比如 ...
- dp学习笔记(各种dp,比较杂)
HDU1176 中文题意不多解释了. 建一个二维dp数组,dp[ i ][ j ]表示第 i 秒落在 j 处一个馅饼.我们需要倒着DP,为什么呢,从 0秒,x=5处出发,假如沿数组正着往下走,终点到哪 ...
- 动态 DP 学习笔记
不得不承认,去年提高组 D2T3 对动态 DP 起到了良好的普及效果. 动态 DP 主要用于解决一类问题.这类问题一般原本都是较为简单的树上 DP 问题,但是被套上了丧心病狂的修改点权的操作.举个例子 ...
- [总结] 动态DP学习笔记
学习了一下动态DP 问题的来源: 给定一棵 \(n\) 个节点的树,点有点权,有 \(m\) 次修改单点点权的操作,回答每次操作之后的最大带权独立集大小. 首先一个显然的 \(O(nm)\) 的做法就 ...
随机推荐
- LintCode笔记 - 145.大小写转换 - 极简之道 - 最短代码
这道题目一眼就能看出是送分题,当然在这里也不谈高难度的实现逻辑,肯定有同学会想直接用自带函数实现不就可以了吗? 对的,就是这么简单,然而今天的重点是如何把代码简写到最短. 本文章将带你把代码长度从 一 ...
- 京东秋招提前批初试--java开发工程师
1,自我介绍,学过的课程有哪些 2,介绍一下java的内存结构和内存模型(jvm和jmm) 3,对于NIO有没有了解?作用是什么?(基于通道和缓冲区的I/O方式,用的是DirectByteBuffer ...
- bzoj4716假摔
bzoj4716假摔 题意: 给出一个矩阵,求这个矩阵中权值和第k小的长在xmin到n之间,宽在ymin到m之间的子矩阵.n,m≤1000,k≤250000. 题解: 首先求出长为xmin,宽为ymi ...
- J.U.C体系进阶(一):juc-executors 执行器框架
Java - J.U.C体系进阶 作者:Kerwin 邮箱:806857264@qq.com 说到做到,就是我的忍道! 主要内容: juc-executors 执行器框架 juc-locks 锁框架 ...
- SpringMVC集成Mybatis
1.pom.xml中添加引入架包 <dependency> <groupId>mysql</groupId> <artifactId>mysql-con ...
- xss利用
xss盗取cookie 什么是cookie cookie是曲奇饼,啊开个玩笑,cookie是每个用户登录唯一id和账号密码一样可以登录到网站,是的你没有听错cookie可以直接登录,至于服务器怎么设置 ...
- PHP : CodeIgniter mysql_real_escape_string 警告
版本 CodeIgniter 3 PHP 5.4 感谢万能的stackoverflow. 得修改CodeIgniter的源码. ./system/database/drivers/mysql/mysq ...
- JS内存机制
在看JS内存机制之前我们先来看一下JS是门什么样的语言,他又有哪些变量类型. 动静态,强弱类型 静态:在使用之前就需要确认其变量数据类型. 动态:在运行过程中需要检查数据类型. 强类型:不支持隐式类型 ...
- [并发编程] -- 内存模型(针对JSR-133内存模型)篇
并发编程模型 1.两个关键问题 1)线程之间如何通信 共享内存程之间共享程序的公共状态,通过写-读内存中的公共状态进行隐式通信 消息传递程之间没有公共状态,线程之间必须通过发送消息来显式进行通信 2) ...
- Python 实现将numpy中的nan和inf,nan替换成对应的均值
nan:not a number inf:infinity;正无穷 numpy中的nan和inf都是float类型 t!=t 返回bool类型的数组(矩阵) np.count_nonzero( ...