问题
分析
分为两个阶段 第一个阶段使用maxv[i,j]和unique[i,j]两个二维数组使用动态规划计算[i,j]内的最大值和是否包含重复数字,他们联合起来可以判断区间[i,j]内是否包含一组连续的1-j-i+1的套娃
第二个阶段计算ans[i],就是前i个套娃合并(可以合并成多组)的最小代价,不能合并就是INF,期间使用dfs计算区间[i,j]合并成一组的代价,把区间分为两个部分[i,k]和[k+1,j],分别计算他们的代价,再计算合并的代价。计算ans[i]时使用刷表法,如果[i,j] (1<=i<j<=n)可以合并成一套,那么就更新ans[j]
合并两组的代价,总的个数-不移动的个数,m1=minvalue(i,k),m2=minvalue(k+1,j),m=max(m1,m2),大小小于m的套娃不用动
测试用例
in:9
1 2 3 4 5 6 1 2 3
out:7
in:9
1 2 3 6 5 4 1 2 3
out:7
#include<iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <set>
using namespace std;
const int maxn=505,INF=0x3f3f3f3f;
int n,s[maxn],maxv[maxn][maxn],minv[maxn][maxn],uniq[maxn][maxn],dp[maxn][maxn];
int pre[maxn],pos[maxn],ans[maxn];
//计算区间[i,j]叠成一组的最小操作次数
int DFS(int left,int right){
int &ans=dp[left][right];
if(ans<INF) return ans;
if(left==right) return ans=0;
for(int i=left;i<right;++i){
//代价是区间内的套娃个数-不移动的套娃,不移动的套娃个数=尺寸小于两组套娃最小值中最大值的套娃
int t=max(minv[left][i],minv[i+1][right]),num=0;
if(minv[left][i]<t){
for(int j=left;j<=i;++j)
if(s[j]<t) ++num;
}else{
for(int j=i+1;j<=right;++j)
if(s[j]<t) ++num;
}
ans=min(ans,DFS(left,i)+DFS(i+1,right)+right-left+1-num);
}
return ans;
}
//两阶段dp,首先使用dp找到所有可能的划分
//在使用dp计算所有[i,j]中的套娃合成的最小代价
int main(void){
while(cin>>n){
memset(pos,-1,sizeof(pos));
for(int i=1;i<=n;++i){
scanf("%d",&s[i]);
pre[i]=pos[s[i]]; //上一个与n的值s[n]相同的数字的位置
pos[s[i]]=i;
}
for(int i=1;i<=n;++i){
for(int j=i;j<=n;++j) {
// maxv[i][j] = 0; //[i,j]区间的最大值
uniq[i][j] = 0; // [i,j]中的值是否重复
// minv[i][j]=INF; //[i,j]中的最小值
dp[i][j]=INF; //代价
}
}
//检测子区间是否有重复数字
for(int i=1;i<=n;++i){
uniq[i][i]=1;
maxv[i][i]=minv[i][i]=s[i];
}
for(int i=1;i<n;++i){
for(int j=i+1;j<=n;++j){
maxv[i][j]=max(maxv[i][j-1],s[j]);
minv[i][j]=min(minv[i][j-1],s[j]);
if(uniq[i][j-1] && pre[j]<i) uniq[i][j]=1;
}
}
//计算代价,ans[i]代表前i个套娃合并(有可能是多组)的最小操作次数
memset(ans,INF,sizeof(ans));
ans[0]=0;
for(int i=1;i<=n;++i){
for(int j=i;j<=n;++j){
if(uniq[i][j] && maxv[i][j]==j-i+1 && ans[i-1]<INF) //[i,j]是一组
ans[j]=min(ans[j],ans[i-1]+DFS(i,j));
}
}
if(ans[n]<INF) printf("%d\n",ans[n]);
else printf("impossible\n");
}
return 0;
}