[BZOJ2616][Thu Summer Camp2016]成绩单

BZOJ传送门

简易题意

现在有一列数,有$n$个元素。每次操作选择一个连续的区间,删除它,代价为$a+b\times (max-min)^2$,其中$max$和$min$表示这段区间的最大值和最小值。

数据范围

$n\leq50,a\leq100,b\leq10,w_i\leq1000$

题解

这题第一眼是一个区间DP,$dp_{l,r}$表示区间$[l,r]$被删去的最小代价。

然而菜鸡的我并不会区间DP。

于是我们我们可以想到把区间DP转化为序列DP。

具体的,我们倒序枚举$l$,之后顺序枚举$r$,每次往决策区间里面添加一个数(在本次操作中被删除)或添加一个整段区间(在之前的某次操作中被删除)。

对于每个确定的$l$,我们设$dp'_{r,v1,v2}$表示当前确定到了第$r$个数,当前决策区间(此次删除的数)中最大值为$v1$,最小值为$v2$的最小代价。

显而易见,$dp_{l,r}=min\{dp'_{r,v1,v2}+b\times calc(v1,v2)+a\}$。

( 其中$v1,v2$需枚举 )。

对于每个$dp'$的转移,我们可以使用类似背包的东西。

具体的,$dp'{r,v1,v2}$可以被用来更新$dp'{r+1,max(v1,w_{r+1}),min(v2,w_{r+1})}$,以及$dp'_{k,v1,v2}$。

我们可以写出如下转移式(dp数组需要取最小值)。

$$
dp'{r+1,max(v1,w{r+1}),min(v2,w_{r+1})}=dp'_{r,v1,v2}
$$

$$
dp'{k,v1,v2}=dp'{r,v1,v2}+dp_{r+1,k}
$$

之后输出$dp_{1,n}$作为答案即可。

权值我们可以使用离散化来保证数组大小在$50$以内。

代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
template<typename __T>
inline void read(__T &x)
{
    x=0;
    int f=1;char c=getchar();
    while(!isdigit(c)){if(c=='-')   f=-1;c=getchar();}
    while(isdigit(c))   {x=x*10+c-'0';c=getchar();}
    x*=f;
}
int n,a,b;
int hs[55],tot=0;
int s[55];
int dp[55][55];
int ndp[55][55][55];
int ca(int a,int b)
{
    return (a-b)*(a-b);
}
int main()
{
    read(n);
    read(a);
    read(b);
    for(int i=1;i<=n;i++)
    {
        read(s[i]);
        hs[tot++]=s[i];
    }
    sort(hs,hs+tot);
    tot=unique(hs,hs+tot)-hs;
    for(int i=1;i<=n;i++)
        s[i]=lower_bound(hs,hs+tot,s[i])-hs;
    memset(dp,63,sizeof(dp));
    for(int l=n;l>=1;l--)
    {
        dp[l][l]=a;
        memset(ndp,63,sizeof(ndp));
        ndp[l][s[l]][s[l]]=0;
        for(int r=l;r<=n;r++)
        {
            for(int v1=0;v1<tot;v1++)
                for(int v2=0;v2<=v1;v2++)
                    dp[l][r]=min(dp[l][r],ndp[r][v1][v2]+b*ca(hs[v1],hs[v2])+a);
            if(dp[l][r]>1000000000) continue;
            for(int v1=0;v1<tot;v1++)
                for(int v2=0;v2<=v1;v2++)
                {
                    if(ndp[r][v1][v2]>1000000000)   continue;
                    ndp[r+1][max(v1,s[r+1])][min(v2,s[r+1])]=min(ndp[r+1][max(v1,s[r+1])][min(v2,s[r+1])],ndp[r][v1][v2]);
                    for(int j=r+1;j<=n;j++)
                        ndp[j][v1][v2]=min(ndp[j][v1][v2],ndp[r][v1][v2]+dp[r+1][j]);
                }
        }
    }
    printf("%d\n",dp[1][n]);
    return 0;
}

参考

没有

猜你喜欢

转载自www.cnblogs.com/ranwen/p/9114945.html