浅谈区间DP
什么是区间 DP
顾名思义,就是对区间进行 DP 操作 。
为什么需要区间 DP
某类有序事件中前若干个事 件的子答案”不再能支撑状态转移,我们需要去寻找“从某个元素起到另一个元素结束所包含所 有的(连续)元素的子答案”作为状态。 -by 题单区间与环形动态规划。
如何想到区间 DP
题目描述
略
这道题乍一眼像是贪心,实际上不是,因为在合并的是相邻的两堆石子,而贪心的话应该是任意两堆。
对题目进行研读,可以发现区间
\(\begin{bmatrix} 1 ,n \end{bmatrix}\)
的是最后合并成了一堆石子,对于这个区间进行操作。
说白了,区间 DP 就是对序列的一系列操作。
可以先枚举区间长度 $ len $,以及区间的 $ l $,通过 $ len,l$ 得到 \(r\) 。
区间操作前缀和也是十分常见的,对于此题,可以对其进行前缀和处理 。
时间复杂度 \(O(n^3)\)
代码
#include<bits/stdc++.h>
const int N=1e3+500;
using namespace std;
int dp[N][N],ai[N],n,sum[N];
int main()
{
memset(dp,127,sizeof(dp));
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&ai[i]);
sum[i]=sum[i-1]+ai[i];//前缀和
dp[i][i]=0;
}
for(int len=2;len<=n;len++)//枚举len
{
for(int l=1;l<=n-len+1;l++)//枚举l
{
int r=l+len-1;//得到r
for(int k=l;k<r;k++)
{
dp[l][r]=min(dp[l][r],dp[l][k]+dp[k+1][r]+sum[r]-sum[l-1]);
}
}
}
printf("%d",dp[1][n]);
return 0;
}
在刚刚的介绍中,我提及了环形动态规划。
对于一个环进行区间操作,又该怎么办?
容易想到对进行一定的赋值,对其回到环的起点但这样操作是否容易实现 ?
在这里,介绍环间中 DP 的一种重要形式,将环从任意的一个地方断开,复制一倍在末端。
时间复杂度 $ O(n^3)$ 。
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int SIZE=1e3;
int dp[SIZE][SIZE],sum[SIZE];
int n,ans,l,r;
signed main()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++) scanf("%lld",&sum[i]),sum[i+n]=sum[i];//复制
for(int k=2;k<=n+1;k++)
for(l=1;l+k<=2*n+1;l++)
{
r=k+l-1;
for(int mid=l+1;mid<=l+k-2;mid++)
dp[l][r]=max(dp[l][r],dp[l][mid]+dp[mid][r]+sum[l]*sum[mid]*sum[r]);
}
for(int i=1;i<=n;i++) ans=max(ans,dp[i][n+i]);//其实本质是一样的,就多了断开的操作
printf("%lld",ans);
return 0;
}

浙公网安备 33010602011771号