【算法】区间DP
【题解】
第二个DP可以预处理mex优化到O(nM+n2),不过我懒……
第一个DP有另一种写法:不预处理,在一个n2取出来的的区间中,枚举决策点从左到右时,保留左最小值的可保留数不严格单调递增,保留右最小值的可保留数不严格单调递减,均摊O(1)。
???
【细节】
f[0]=0初始化
inf+inf+inf就会爆int
区间第二重循环是i=1...n-p,否则有可能爆数组边界。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=,inf=0x3f3f3f3f;
int dp[maxn][maxn],f[maxn],sum[maxn][maxn],ms[maxn][maxn],n,m,A[maxn];
int calc(int a,int b,int c,int d)
{
int m1=ms[a][b],m2=ms[c][d];
int ans=inf;
ans=(b-a+)-(sum[b][m2]-sum[a-][m2])+(d-c+);
ans=min(ans,(d-c+)-(sum[d][m1]-sum[c-][m1])+(b-a+));
return ans;
}
bool B[maxn];
bool mex(int a,int b)
{
memset(B,,sizeof(B));
for(int i=a;i<=b;i++)if(B[A[i]])return ;else B[A[i]]++;
bool ok=;
if(!B[])return ;
for(int i=;i<=m;i++)
{
if(ok&&B[i])return ;
if(B[i-]&&!B[i])ok=;
}
return ;
}
int main()
{
scanf("%d",&n);
m=;
for(int i=;i<=n;i++){scanf("%d",&A[i]);m=max(m,A[i]+);}
for(int i=;i<=n;i++)
{
for(int j=;j<=A[i];j++)sum[i][j]=sum[i-][j];
for(int j=A[i];j<m;j++)sum[i][j]=sum[i-][j]+;
ms[i][i]=A[i];
for(int j=i+;j<=n;j++)ms[i][j]=min(ms[i][j-],A[j]);
}
for(int p=;p<=n-;p++)
{
for(int i=;i<=n-p;i++)//
{
int j=i+p;
dp[i][j]=inf;
for(int k=i;k<j;k++)if(dp[i][k]<inf&&dp[k+][j]<inf)//
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+][j]+calc(i,k,k+,j));
}
}
memset(f,0x3f,sizeof(f));
f[]=;//初始化!!!
for(int i=;i<=n;i++)
{
for(int j=;j<i;j++)if(f[j]<inf-)
{
if(mex(j+,i))
{
f[i]=min(f[i],f[j]+dp[j+][i]);
}
}
}
if(f[n]>inf-)printf("Impossible");
else printf("%d",f[n]);
return ;
}