一、区间DP

顾名思义区间DP就是在区间上进行动态规划,先求出一段区间上的最优解,在合并成整个大区间的最优解,方法主要有记忆化搜素递归的形式。

顺便提一下动态规划的成立条件是满足最优子结构无后效性

二、经典例题分析:

1.石子合并:

一条直线上有N堆石子,现在要将所有石子合并成一堆,每次只能合并相邻的两堆,合并花费为新合成的一堆石子的数量,求最小花费。

分析

一般看到最小,最短这样的字眼,可以往动态规划的方向思考,显然当我任选两堆合并时,只会影响下一次选择,不会对再后来的选择有影响,这时候我们几乎可以确认用DP了

1堆:花费为0.

2堆:花费为sum[2].

3堆:花费为min(a[1]+a[2],a[2]+a[3])+sum[3].

如果我们有N堆,最后一次合并一定是将两堆合并成一堆,那两堆一定都是最少花费,由此往下想:那两堆肯定也是有最优的两堆合并,这样就是一个递归过程。

所以我们可以想办法找出每个区间划分为两个最少花费区间的点,这就是第一个模型:

第一个模型:将大区间划分为两个小区间。

此题我们规定dp[i][j]为合并第i堆到第j堆的最小花费。

DP方程为:dp[i][j]=min(dp[i][k]+dp[k+1][j])+sum[j]-sum[i-1].

memset(dp,Ox3f,sizeof(dp))
for(int i=1;i<=dp.size();i++){
dp[i][i]=0;//将一堆石子合并花费为0
sum[i][i]=stones[i];//合并第i堆到第j堆的花费。
}
for(int len=1;len<n;len++){//区间长度
for(int i=1;i<=n&&i+len<=n;i++{//区间起点
int j=i+len;//区间终点
for(int k=i;k<=j;k++){//区间划分点
sum[i][j]=sum[i][k]+sum[k][j];
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[i][j];
}
}
}

我们再看回 LeetCode合并石子:

与例题不同的是此题是给定一个K,要将K长度内的石子合并(我们知道石子是一行排列),换汤不换药,仍然是区间DP解法,只是将相邻两堆改成了相邻K堆:

本来我们只需将区间划分为任意两部分,如今我们要将区间这样划分:一部分长度为K-1,另一部分看作为一整堆,这样最后才能合并为一整堆,所以我们要做的改变只是将划分点每次移动的距离由1变为K-1,

保证左边部分为K-1的整数倍。

由此我们得出要有结果,stones长度必须满足:j-i%K-1==0。

int len=stones.length;
if((len-1)%(K-1)!=0)return -1;
int[][] dp=new int[len][len];
int[] sum=new int[len+1];
for(int i=0;i<len;i++){
dp[i][i]=stones[i];
sum[i+1]=sum[i]+stones[i];
}
for(int j=1;j<len;j++){
for(int i=j-1;i>=0;i--){//只需考虑能最后能求解的子区间,因为最后不会用到不能求解的
dp[i][j]=dp[i][i]+dp[i+1][j];//划分i,i+1-j两部分
for(int k=i+K-1;k<j;k+=K-1){
dp[i][j]=Math.min(dp[i][j],dp[i][k]+dp[k+1][j]);//DP方程:其中若i-j不是K-1的整数倍则继续DP时不会用到DP[i][j]。
}
if((j-i)%(K-1)==0){//最后合并。
dp[i][j]+=sum[j+1]-sum[i];
}
}
}
return dp[0][len-1]-sum[len];//每次小区间合并时都已计算合并时的花费所以需要减去。

这就是对第一模型的例题分析,都是将大区间划分为小区间,求取最优解。

2.括号匹配:

给定一个括号()[]组成的字符串,你要找到一个最长的合法的子序列,对于一个字序列,其中的括号一定有另一个相对应。

我们先尝试用上一模型解决试试:

规定dp[i][j]为第i个字符到第j个字符之间的最长匹配序列。

长度为N时,我们可以先检测a[i]和a[j]是否匹配,如果匹配,dp[i][j]=dp[i+1][j-1]+2,否则,就可以按第一模型处理,从任意位置划分为两个区间:dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j].

for (int len = 2; len <= n; len++)
{
for(int i = 1, j = len; j <= n; i++, j++)
{
if((a[i]=='('&&a[j]==')') || (a[i]=='['&&a[j]==']'))//i,j位置能匹配
dp[i][j] = dp[i+1][j-1] + 2;
for (int k = i; k < j; k++)//任意位置划分为两个区间
if(dp[i][j] < dp[i][k] + dp[k+1][j])
dp[i][j] = dp[i][k] + dp[k+1][j];
}

但这不是我们想要的第二个模型,假设我们只考虑[i,j]是由[i+1,j]在前面加一个字符的情况,如果a[i+1]到a[j]没有和a[i]匹配的,那么dp[i][j]=dp[i+1][j],如果匹配(i<k<=j),那么dp[i][j]=max(dp[i][j],dp[i+1][k-1]+dp[k+1][j]+2);

比如:[xxxxx]yyyyy通过括号分成两个子串.

所以第二种模型就是根据匹配信息把区间划分为[i+1,k-1]和[k+1,j]

for (int len = 2; len <= n; len++)
{
for(int i = 1, j = len; j <= n; i++, j++)
{
dp[i][j] = dp[i+1][j];//不能匹配
for (int k = i; k <= j; k++)//能匹配
if((a[i]=='('&&a[k]==')') || (a[i]=='['&&a[k]==']'))
dp[i][j] = max(dp[i][j], dp[i+1][k-1] + dp[k+1][j] + 2);
}
}

Cheapest Palindrome:

n个字符组成的长度为m的字符串,给出增删字符的花费,可在字符串的任意位置增删字符,求把字符串修改为回文串的最小花费。

例:n=3,m=4组成abcd,

a:1000,1000,b:350,700,c:200 800

这题分四种情况:假设有字符串:Xxxx...xxY

1.去掉X,取xx..xY回文;

2.去掉Y,取Xx...x回文;

3.在左边加上X,Xxx...xYX回文;

4.在右边加上Y,取YXxx...x回文;

规定dp[i][j]为把[i..j]区间改为回文串的最小花费

我们得出DP方程:dp[i][j]=min(dp[i][j],dp[i+1][j]+min(add[a[i]-'a']+sub[a[i]-'a'])),//增删X时

dp[i][j]=min(dp[i][j],dp[i][j-1]+min(add[a[i]-'a']+sub[a[i]-'a'])),//增删Y时

for (int len = 2; len <= m; len++)
for(int i = 1, j = len; j <= m; i++, j++)
{
dp[i][j] = min(dp[i][j], min(add[a[i]-'a'],sub[a[i]-'a']) + dp[i+1][j]);//增删X时
dp[i][j] = min(dp[i][j], dp[i][j-1] + min(add[a[j]-'a'],sub[a[j]-'a']));//增删Y时
if (a[i] == a[j])
{
if (len==2)
dp[i][j] = 0;
else
dp[i][j] = min(dp[i][j], dp[i+1][j-1]);//相等时就等于[[i+1..j-1]回文的长度。
}
}

这就是我们第三个模型只考虑左右边界,不需要枚举区间k->[i,j]

4.总结:

区间DP最重要的时理解大区间和小区间之间的联系,才能写出DP方程,其实也可以将其看成是一个递归过程大区间由小区间得出,小区间由小小区间得出。

区间DP(力扣1000.合并石头的最低成本)的更多相关文章

  1. Leetcode1000 合并石头的最低成本 区间DP

    有 N 堆石头排成一排,第 i 堆中有 stones[i] 块石头. 每次移动(move)需要将连续的 K 堆石头合并为一堆,而这个移动的成本为这 K 堆石头的总数. 找出把所有石头合并成一堆的最低成 ...

  2. [Swift]LeetCode1000. 合并石头的最低成本 | Minimum Cost to Merge Stones

    There are N piles of stones arranged in a row.  The i-th pile has stones[i] stones. A move consists ...

  3. [IOI1998] Polygon (区间dp,和石子合并很相似)

    题意: 给你一个多边形(可以看作n个顶点,n-1条边的图),每一条边上有一个符号(+号或者*号),这个多边形有n个顶点,每一个顶点有一个值 最初你可以把一条边删除掉,这个时候这就是一个n个顶点,n-2 ...

  4. 洛谷1880 区间dp+记忆化搜索 合并石子

    题目网址:https://www.luogu.com.cn/problem/P1880 题意是:给定一个序列,最小规则是相邻两个值的合并,开销是他们的和,将整个序列合并成一个值的情况下,求解该值的最小 ...

  5. 以石子合并为例的区间DP

    区间DP,是一类具有较为固定解法的DP,一般的思路都是: first.初始化区间长度为1的情况(一般区间长度为1的较易于初始化) second. for(枚举区间长度2~n){ for(枚举左端点){ ...

  6. 区间DP小结

    也写了好几天的区间DP了,这里稍微总结一下(感觉还是不怎么会啊!). 但是多多少少也有了点感悟: 一.在有了一点思路之后,一定要先确定好dp数组的含义,不要模糊不清地就去写状态转移方程. 二.还么想好 ...

  7. hdu 4283 区间dp

    You Are the One Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)T ...

  8. cdoj 1131 男神的礼物 区间dp

    男神的礼物 Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.uestc.edu.cn/#/problem/show/1131 Descr ...

  9. 洛谷P1040 加分二叉树(区间dp)

    P1040 加分二叉树 题目描述 设一个n个节点的二叉树tree的中序遍历为(1,2,3,…,n),其中数字1,2,3,…,n为节点编号.每个节点都有一个分数(均为正整数),记第i个节点的分数为di, ...

随机推荐

  1. Nginx笔记总结十四: nginx反向代理,用内网域名转发

    user www www; worker_processes ; error_log logs/error.log; pid logs/nginx.pid; worker_rlimit_nofile ...

  2. CHI 2015大会:着眼于更加个性化的人机交互

    2015大会:着眼于更加个性化的人机交互" title="CHI 2015大会:着眼于更加个性化的人机交互"> 本周,人机交互领域的顶级盛会--2015年ACM C ...

  3. 2020 倒计时 1 天,Python 工程师找工作更难了?

    Python 是最神奇的编程语言. 无意引战,我说的是"神奇",不是"最好",并不想去"撼动" PHP 的地位.               ...

  4. Data Visualization and D3.js 笔记(1)

    课程地址: https://classroom.udacity.com/courses/ud507 什么是数据可视化? 高效传达一个故事/概念,探索数据的pattern 通过颜色.尺寸.形式在视觉上表 ...

  5. Tesseract OCR使用介绍

    #Tesseract OCR使用介绍 ##目录[TOC] ##下载地址及介绍 官网介绍:http://code.google.com/p/tesseract-ocr/wiki/TrainingTess ...

  6. curator配置及使用

    1.action.yml --- actions: 1: action: index_settings options: index_settings: index: routing.allocati ...

  7. Linux下无法生成core文件的解决办法

    1.检查ulimit [root ~]# ulimit -c 0 0:表示禁止生成core文件,此时需要执行ulimit -c unlimited(临时生效),或者在.bashrc中添加“ulimit ...

  8. ES Templates push

    { "order": 0, "template": "connector-connection*", "settings" ...

  9. JavaScript是如何工作的(一)

    简评:JavaScript 是越来越受欢迎了,很多团队都在采用这些语言工作.前端.后端.嵌入式设备等等,都可以看见它的身影.虽然我们知其然,但又知其所以然吗? 大家应该都知道 JavaScript 是 ...

  10. VUE深入浅出(学习过程)

    VUE 2020年02月26日06:27:10 复习过Java8新特性之后开始学习VUE. 了解node了之后,来了解一下VUE.针对于学习VUE用什么开发工具这个问题上,我这里有vsCode和web ...