一道(对我来说)比较迷的区间dp,后来看到大佬的题解才明白是怎么回事
我们都知道传统的区间dp写法是这样的
for(int len = 1;len<=n;len++){//枚举长度
for(int j = 1;j+len<=n+1;j++){//枚举起点,ends<=n
int ends = j+len - 1;
for(int i = j;i<ends;i++){//枚举分割点,更新小区间最优解
dp[j][ends] = min(dp[j][ends],dp[j][i]+dp[i+1][ends]+something);
}
}
}
首先为什么判定是区间dp呢?
一是看数据在500以内,暗示时间复杂度 n^3;
二是存在明显的区间合并操作:左右相等后合并为一个数+1(something)
而这一题和普通的区间dp有什么区别呢
首先,维护的是区间最小值而非前缀和,因而dp初值要设为无穷大
其次,我们需要维护另一个类似于案例中dp数组一样的功能,即合并区间再加一
代码如下:
#include<iostream>
using namespace std;
const int N = 5e2+5;
int dp[N][N],now[N][N],a[N];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dp[i][j] = 0x3f3f3f3f;
for(int i=1;i<=n;i++)
{
dp[i][i] = 1;
cin>>a[i];
now[i][i] = a[i];
}
for(int len=1;len<=n;len++)
{
for(int i = 1;i+len<=n+1;i++)
{
int end = i+len-1;
for(int j = i;j < end;j++){//now充当原先dp的作用,现在的dp充当记录者
if(dp[i][j]==1&&dp[j+1][end]==1&&now[i][j]==now[j+1][end])
{
dp[i][end] = 1;now[i][end] = now[i][j]+1;
}
dp[i][end] = min(dp[i][end],dp[i][j]+dp[j+1][end]);
}
}
}
cout<<dp[1][n]<<endl;
}