Uva 1579 Matryoshkas

 

 

 

 

 

这道题应该是dp(有点空洞)我们想,f(i)表示前i个套娃合并成多个套娃组所应操作的最小次数

f(i)=min(f(j)+dp(j+1,i));

这里自然的引出了dp(j+1,i)什么是dp(j+1,i)呢?

dp(j+1,i)合并成一个套娃所拥有的最小代价。

这里又不自然的dp(j+1,i)=min{dp(j+1,k)+dp(k,i)+操作数}

那么操作数又是什么呢,

 对于要合并的两个区间[l,k]&[k+1,r],最后一步把他们合并的费用是多少呢?假设m1=min[l,k],m2=min[k+1,r],则不用打开的套娃数为这个区间中小于max(m1,m2)的数的个数。 
 问题完美解决。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 500+10
#define inf 0x3fffffff
using namespace std;
int a[N],mx[N][N],mn[N][N],p[N][N];
int n;
int f1[N][N],f2[N];
struct node{
    int x,id;
    inline bool operator < (const node & a)const{return x<a.x;}
};
void init()
{
    memset(mx,0,sizeof(mx));memset(mn,63,sizeof(mn));
    memset(p,0,sizeof(p));
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)mn[i][i]=mx[i][i]=a[i],p[i][i]=1;
    int flag;
    for(int i=n;i>=1;i--)
        for(int j=i+1;j<=n;j++)
        {
            mx[i][j]=max(mx[i][j-1],a[j]);
            mn[i][j]=min(mn[i][j-1],a[j]);
            flag=1;
            for(int k=j-1;k>=i;k--)
            {
                if(a[k]==a[j]){flag=0;break;}
            }
            if(flag&&p[i][j-1])p[i][j]=1;
        }
//             for(int i=1;i<=n;i++)
//         for(int j=1;j<=n;j++)
//         {
//             printf("%d %d %d\n",i,j,p[i][j]);
//        }
}
int dp(int x,int y)
{
    if(f1[x][y]<10000) return f1[x][y];
    if(x==y){return f1[x][y]=0;}
    int num;
    node t[N];
    for(int i=x;i<=y;i++)t[i-x+1].x=a[i],t[i-x+1].id=i;
    sort(t+1,t+1+y-x+1);
    for(int i=x;i<y;i++)
    {
        int mm=max(mn[x][i],mn[i+1][y]);
        num=0;
        for(int j=1;j<=y-x+1;j++)
        {
            if(t[j].x==mm) break;
            if(t[j].x<mm) num++;
        }
        num=y-x+1-num;
        f1[x][y]=min(f1[x][y],dp(x,i)+dp(i+1,y)+num);
    }
    return f1[x][y];
}
void slove()
{
    memset(f1,63,sizeof(f1));
    memset(f2,63,sizeof(f2));
//    for(int i=1;i<=n;i++)
//         for(int j=i;j<=n;j++)
//     {
//         if(mx[i][j]==j-i+1&&p[i][j]) 
//             printf("%d %d %d\n",i,j,dp(i,j));
//     }
    f2[0]=0;
    for(int i=1;i<=n;i++)
        for(int j=0;j<i;j++)
    {

        if(p[j+1][i]&&i-j==mx[j+1][i])
        f2[i]=min(f2[i],f2[j]+dp(j+1,i));
//        printf("%d %d %d\n",j+1,i,dp(j+1,i));
    }
    if(f2[n]>10000)printf("impossible\n");
    else printf("%d\n",f2[n]);

}
int main()
{
    while(scanf("%d",&n)==1)
    {
        init();
        slove();
    }
    return 0;
}

p[i][j]表示i——j这段区间是否有重复的元素,小技巧p[i][j]=(p[i][j-1]&&a[j]!=任何i————j-1的数)

posted @ 2017-10-30 14:16  star_eternal  阅读(239)  评论(0编辑  收藏  举报