浅谈区间DP

什么是区间 DP

顾名思义,就是对区间进行 DP 操作 。

为什么需要区间 DP

某类有序事件中前若干个事 件的子答案”不再能支撑状态转移,我们需要去寻找“从某个元素起到另一个元素结束所包含所 有的(连续)元素的子答案”作为状态。 -by 题单区间与环形动态规划。

如何想到区间 DP

P1775 石子合并(弱化版)

题目描述

这道题乍一眼像是贪心,实际上不是,因为在合并的是相邻的两堆石子,而贪心的话应该是任意两堆。

对题目进行研读,可以发现区间
\(\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;
}

在刚刚的介绍中,我提及了环形动态规划。
对于一个环进行区间操作,又该怎么办?

容易想到对进行一定的赋值,对其回到环的起点但这样操作是否容易实现 ?

P1063 [NOIP2006 提高组] 能量项链

在这里,介绍环间中 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;
}
posted @ 2023-09-26 17:48  奈绪  阅读(47)  评论(0)    收藏  举报