Tag : 「区间 DP」、「动态规划」 有 n 个气球,编号为 0 到 n - 1,每个气球上都标有一个数字,这些数字存在数组 nums 中。 现在要求你戳破所有的气球。 1*8*1 = 167 示例 2: 输入:nums = [1,5] 输出:10 提示: n = nums.length 1 <= n <= 300 0 <= nums[i] <= 100 区间 DP 定义 f[l][r] 为考虑将 (l, r) 范围内(不包含 l 和 r 边界)的气球消耗掉,所能取得的最大价值。 + f[k][r] + arr[l] \times arr[k] \times arr[r]), k \in (l, r) 为了确保转移能够顺利进行,我们需要确保在计算 f[l][r] 的时候,区间长度比其小的 因此我们可以采用先枚举区间长度 len,然后枚举区间左端点 l(同时直接算得区间右端点 r)的方式来做。
当然是把区间进行分割:下面列举分割 i,[i+1,j] [i,i+1],[i+2,j] '''''' [i,i+k],[i+k+1,j] k>=0&&k<j-i 怎么求f[i][j]呢? ;len<=n;len++)//长度为1的都是0,不需要从1开始遍历 { for(int left=1;left+len-1<=n;left++)//从1开始生成长度为len的区间 ;并且不断更新区间——滑动区间。
做了几题区间动态规划的题目,觉得区间动态规划的题目是有点难的。区间DP大概是这一类的动态规划,在一个线性的数据上对区间进行状态转移,dp[i][j]表示i到j的区间。 dp[i][j]可以由子区间的状态转移而来,关键是dp[i][j]表示的是什么,然后去找dp[i][j]和子区间的关系。要知道,在求dp[i][j]之前,i到j之间的子区间都已经求出来最优解。 dp[i+1][j-1] + 2 : dp[i+1][j-1] ) ) );就是在dp[i+1][j],dp[i][j-1],dp[i+1][j-1]三个子区间求最大值。 这两道的题目的子区间只涉及到dp[i+1][j],dp[i][j-1],dp[i+1][j-1],并没有在i到j之间进行区间划分,这是因为回文串的特性。 id=2955 这是一道简单的区间划分dp题目 求最长的匹配括号的长度,划分区间是没有条件的,从i到j区间内任何一点都可以划分,dp[i][j]=max(dp[i][k]+dp[k+1][j])
区间动态规划问题一般都是考虑,对于每段区间,他们的最优值都是由几段更小区间的最优值得到,是分治思想的一种应用,将一个区间问题不断划分为更小的区间直至一个元素组成的区间,枚举他们的组合 ,求合并后的最优值 设F[i,j](1<=i<=j<=n)表示区间[i,j]内的数字相加的最小代价 最小区间F[i,i]=0(一个数字无法合并,∴代价为0) 每次用变量k(i<=k<=j-1)将区间分为[i,k]和[k+1 ,j]两段 For p:=1 to n do // p是区间长度,作为阶段。 for i:=1 to n do // i是穷举的区间的起点 begin j:=i+p-1; // j是 区间的终点,这样所有的区间就穷举完毕 if j>n then break; // 这个if很关键 for k:= i to j-1 do // 状态转移,去推出 f[i,j] f[i , j]= max{f[ i,k]+ f[k+1,j]+ w[i,j] } end; 这个结构必须记好,这是区间动态规划的代码结构
一.什么是区间dp? 顾名思义:区间dp就是在区间上进行动态规划,求解一段区间上的最优解。主要是通过合并小区间的 最优解进而得出整个大区间上最优解的dp算法。 二.核心思路 既然让我求解在一个区间上的最优解,那么我把这个区间分割成一个个小区间,求解每个小区间的最优解,再合并小区间得到大区间即可。 所以在代码实现上,我可以枚举区间长度len为每次分割成的小区间长度(由短到长不断合并),内层枚举该长度下可以的起点,自然终点也就明了了。 } } 三.朴素区间dp(n^3) 1.例题:石子归并1 (1)传送门:戳我呀 (2)转移方程: dp[j][ends] = min(dp[j][ends],dp[j][i]+dp[i+1][ends (2)思路:因为最后都要删掉中间所有的数字,所以我们分隔一个个小区间删数字,合并区间求最小。
文章目录 区间DP 四边形不等式优化 例题 石子合并 回文串 区间DP image.png //朴素DP参考 for (int i = 1; i <= n; i++)dp[i][i]=0; for ( int len = 1; len <= n; len++){ //枚举区间长度 for (int i = 1; i <= n - len; i++){ //枚举区间的起点 int j = i + len; //根据起点和长度得出终点 for(int k = i; k < j; k++) //枚举分割点 dp[i][j] = min(dp[ [i][j] > dp[i][k] + dp[k + 1][j] + cost[i][j]){ dp[i][j] = dp[i][k] + dp[k + 1][j] + cost[i [i][j] = dp[i + 1][j - 1]; else dp[i][j] = min(dp[i + 1][j] + cost[s[i] - 'a'], dp[i][j - 1]
区间DP是一类在区间上进行动态规划的最优问题,一般是根据问题设出一个表示状态的dp,可以是二维的也可以是三维的,一般情况下为二维。 然后将问题划分成两个子问题,也就是一段区间分成左右两个区间,然后将左右两个区间合并到整个区间,或者说局部最优解合并为全局最优解,然后得解。 这类DP可以用常规的for循环来写,也可以用记忆化搜索来写。 = -1) return dp[x][y]; if (x + 1 >= y) return dp[x][y]=0; dp[x][y] = 0x7fffff; for (int i = x + 1; i < y; i++){ dp[x][y] = min(dp[x][y], dfs(x, i) + dfs(i, y) + a[x] * a[i] * a[y]); } return = -1) return dp[x][y]; if (x + 1 >= y) return dp[x][y] = 0; dp[x][y] = 0; for (int i = x; i <
image.png AC代码 #include<bits/stdc++.h> #define x first #define y second #define PB push_back #define mst(x,a) memset(x,a,sizeof(x)) #define all(a) begin(a),end(a) #define rep(x,l,u) for(ll x=l;x<u;x++) #define rrep(x,l,u) for(ll x=l;x>=u;x--) #define IOS i
基本内容通过分治的思想实现DP数组入门例子 NOI1995] 石子合并 - 洛谷 | 计算机科学教育新生态题目要求:给定一圈石头数组,每个石头对应一个权重值,当两个石头合并时组成一个小石头堆,成本为两个石头权重值相加 cuts = [0] + sorted(cuts) + [n]length = len(cuts)# 初始化 DP 数组f = [[0] * length for _ in range(length)] # 动态规划计算最小切割代价for len_interval in range(2, length): # 区间长度 for l in range(length - len_interval):
题目链接 题意 给你两个长度为n的数列a和b,要求你最多翻转一次a序列的一段连续的区间,使得\displaystyle \sum_{i = 1} ^ n a_i*b_i 最大。 分析 首先暴力方法是先枚举区间长度再枚举起点,整个过程是 O(n^2) 的,然后就是如何O(1)求出翻转后的值,先去找了一下每个翻转策略之间有没有冗余部分,发现没有啥可以优化的,想不出怎么快速求值,于是否掉了这个方法 开始思考DP,感觉是个区间DP,按照固有思维,将DP的属性设置为某个区间的最优解,但明显不对啊,因为只能翻转一次,只有一种情况,哪里来的最优解。然后就…卡题了。看了一眼题解,oh,wssb。 就是区间DP,而且就是用一开始想的暴力找,O(1)算的思路,计算的过程用DP使得每次计算可以O(1)完成。 具体方法是: 定义 dp[i][j] 为区间( i , j )翻转后与翻转前的 \displaystyle \sum_{k = i} ^ j a_k*b_k 差值,于是 : dp[i][j] = dp
For example, the sequence // // main.cpp // 区间dp 1001 // // Created by 陈永康 on 16/2/28. // Copyright [i][j]=(dp[i+1][j]+dp[i][j-1]-dp[i+1][j-1]+10007)%10007; if(a[i]==a[j]) dp[i][j]=(dp[i][j]+dp[i+1][j-1]+1+10007)%10007; //dp[i][j]%=10007; } ]区间i到j的回文子串数量,dp[i][j]=dp[i+1][j]+dp[i][j-1]-d[i+1][j-1],dp[i+1][j-1]是重复的部分这是a[i]不等于a[j]的情况,若二者相等,在这个基础上还要加上 dp[i+1][j-1]+1。
区间dp入门——总结+习题+解析 区间dp,顾名思义,在区间上dp,大多数题目的状态都是由区间(类似于dp[l][r]这种形式)构成的,就是我们可以把大区间转化成小区间来处理,然后对小区间处理后再回溯的求出大区间的值 Solution [Java/C++/Python] DP Python, DP, easy to understand import functools class Solution: def : prefix[i + 1] = prefix[i] + stones[i] @functools.lru_cache(None) def dp if i == j: return 0 if m == 1 else -1 if m == 1: return dp (i, j, K) + prefix[j + 1] - prefix[i] return min(dp(i, mid, 1) + dp(mid + 1,
区间dp概述 区间dp,就是在一段区间上进行动态规划,求解一段区间的最优解。最后合并小区间上的最优解从而得到全局最优解的算法。 【问题引入】 石子合并问题 N堆石子摆成一条线。 所以我们可以将数组复制两份,再进行线性区间dp即可。 //区间dp //区间上的最优解 //合并出全局最优解 //环状dp #include<iostream> using namespace std; const int INF = 0x7f7f7f7f 因为最后都要删除所有的数字(除了两端的数字),所以我们可以分割一个个小区间删除数字,然后合并区间求最小。 状态表示:dp[i][j]表示抽出第i张到第j-1张的最小得分。 状态转移方程: dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]+num[i-1]*num[k]*num[j]) //区间dp //抽卡片 //子区间合并出全局最优解 #include
,将区间内的字符全都变成相同的字符。 其实二者是完全一样的问题,一件穿了多少天就相当于在一个固定的区间覆盖了一个字符。 状态转移方程: dp[i][j]=dp[i+1][j]+1; dp[i][j]=min(dp[i][j],dp[i+1][k]+dp[k+1][j]). 我们可以从区间的左端或者右端开始,和区间里的数比较,相等的话就划分区间。 这样的话可以得到全局最优解,注意递推的时候,一定要使子状态都计算过了。 关于区间DP,可以参照这个博客 http://blog.csdn.net/dacc123/article/details/50885903 从左端开始比较: #include <iostream>
For example, the sequence // // main.cpp // 区间dp 1001 // // Created by 陈永康 on 16/2/28. // Copyright ,0,sizeof(dp)); for(int i=1;i<=len;i++) dp[i][i]=1; for(int l=1;l<len;l++ [i][j]++; // else // dp[i][j]+=(dp[i+1][k-1])+1; // } // } dp[i][j]=(dp[i+1][j]+dp[i][j-1]-dp[i+1][j-1 ]+10007)%10007; if(a[i]==a[j]) dp[i][j]=(dp[i][j]+dp[i+1][j-1]+1+
虽然代码很短,确很值得我去思量,第二道区间DP题目 #include <iostream> #include <string.h> #include <stdlib.h> #include <algorithm > #include <math.h> using namespace std; int dp[105][105]; int ans[105]; char s1[105]; char s2[105]; =EOF) { memset(dp,0,sizeof(dp)); int len=strlen(s1+1); for(int j=1;j<=len ;j++) { for(int i=j;i>=1;i--) { dp[i][j]=dp[i+1][j]+1 [i][j]=min(dp[i][j],dp[i+1][k]+dp[k+1][j]); } } } for(int
[i][j]=INF; if(str[i]==str[j]) dp[i][j]=dp[i+1][j-1];//相同则不花费 dp[i][j]=min(dp[i][j], dp[i+1][j]+cost[str[i]-'a']);//处理左边 dp[i][j]=min(dp[i][j],dp[i][j-1]+cost[str[j]-'a']);//处理右边 dp[i][j]=min(dp[i][j],dp[i+1][j-1]+min(C[str[j]-'a'][str[i]-'a'],C[str[i]-'a'][str[j]-'a']) );//两边同时变换中字母 for(int k=0;k<=26;k++) dp[i][j]=min(dp[i][j],dp[i+1][j-1]+C[str[i] -'a'][k]+C[str[j]-'a'][k]); } if(dp[1][len]==INF) cout<<"-1"<<endl;else cout<<dp[1][len]<<endl
分析 DP,dp[i][j]表示第i天到第j天不脱第i天之前的衣服最少需要的衣服数量,那就可以由和第j天穿一样的衣服的第k天转移过来,或者再套一件第j天的衣服。 状态转移方程:dp[i][j]=min(dp[i][k]+dp[k+1][j-1],dp[i][j-1]+1)(i≤k<j,a[j]==a[k]) 算的时候i从大到小算,因为算dp[i][j]时用到了比 i 更大的 k+1 的dp[k+1][j-1]。 代码 #include<cstdio> #include<algorithm> using namespace std; int t,n,a[105],dp[105][105]; int main() (a[j]==a[k]) { dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j-1
题意: 有一根长度为l的木棍,木棍上面有m个切割点,每一次切割都要付出当前木棍长度的代价,问怎样切割有最小代价 在完成这道题目之前,我们先熟悉一下区间DP 对于这道题,状态转移方程dp[i][j]=min (dp[i][k],dp[k][j])+num[j]-num[i] (i<k<j)切记k不能等于i,j 当dp[i][i] dp[j][j]这是等于0的,导致其相加的和必然 小,导致MIN记录的数据有错 [i][i]=0,这必然导致找到的值小于正常的 { tmp=dp[i][k]+dp[k][j]+num[j]-num[i]; =INF)dp[i][j]=MIN; } printf("The minimum cutting is %d. \n",dp[0][n+1]); } return 0; }
欢迎回来~继续我们的DP专题,上一篇我们讲了一个较为复杂的线性DP问题,这一次让我们看一看区间DP问题。 区间DP直观上可以理解成对于一个区间计算最优解的问题。先来看下本题的题目,直接上中文。 区间DP通用的转移方程如下: f(i,j) = min{f[i,k] + f[k+1,j] + cost(i,j) 其中cost为将区间i~j合并起来的代价,可以用前缀和来计算(前缀和传送门)。 区间DP中要比较小心的是阶段的划分,本题中使用区间长度len作为阶段,为什么要选用len呢?单纯的看状态转移方程,我们很难确定如何划分阶段,因为里面没有任何关于len的信息。 对于使用递推的方式来解决DP问题,我们都需要从初始值推导出后续值,比如从0一直到N,而不能反着来。 区间DP也有同样的处理方式,我们必须先从区间长度为0的初始值出发,也就是说,f(i,j)其实可以等价于一维的F(len)。