UVA - 1579 Matryoshka (区间DP+多个DP)

问题

分析

分为两个阶段 第一个阶段使用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;
}
发布了50 篇原创文章 · 获赞 0 · 访问量 689

猜你喜欢

转载自blog.csdn.net/zpf1998/article/details/104162350