1. 程式人生 > >BZOJ.4897.[Thu Summer Camp2016]成績單(區間DP)

BZOJ.4897.[Thu Summer Camp2016]成績單(區間DP)

BZOJ

顯然是個區間DP。令\(f[l][r]\)表示全部消掉區間\([l,r]\)的最小花費。

因為是可以通過刪掉若干子串來刪子序列的,所以並不好直接轉移。而花費只與最大最小值有關,所以再令\(g[l][r][j][k]\)表示將區間\([l,r]\)中的數刪到只剩下權值在\([j,k]\)中的數的最小花費(也就是讓剩下數的最小值為\(j\),最大值為\(k\),最後一次取走\([j,k]\)這些數來刪掉整個\([l,r]\))。為了方便轉移強制右端點\(r\)保留,同整個區間最後一起刪掉。

然後\(f,g\)就可以互相轉移了:\(g[l][r][\min(val_r,j)][\max(val_r,k)]=\min\{g[l][i][j][k]+f[i+1][r-1]\}\)

\(f[l][r]=\min\{f[l][i]+g[i+1][r][j][k]+Cost(j,k)\}\)

複雜度\(O(n^5)\)

//29392kb   1952ms
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define gc() getchar()
#define Sqr(x) ((x)*(x))
typedef long long LL;
const int N=52;

int w[N],ref[N],f[N][N],g[N][N][N][N];

inline int read()
{
    int now=0;register char c=gc();
    for(;!isdigit(c);c=gc());
    for(;isdigit(c);now=now*10+c-'0',c=gc());
    return now;
}

int main()
{
    const int n=read(),A=read(),B=read();
    for(int i=1; i<=n; ++i) w[i]=ref[i]=read();
    std::sort(ref+1,ref+1+n);
    int cnt=std::unique(ref+1,ref+1+n)-ref-1;
    for(int i=1; i<=n; ++i) w[i]=std::lower_bound(ref+1,ref+1+cnt,w[i])-ref;

    memset(f,0x3f,sizeof f), memset(g,0x3f,sizeof g);
    for(int i=1; i<=n; ++i) f[i][i]=A, f[i][i-1]=0, g[i][i][w[i]][w[i]]=0;
    f[n+1][n]=0;
    for(int len=1; len<n; ++len)
        for(int l=1,r; (r=l+len)<=n; ++l)
        {
            for(int i=l; i<r; ++i)
                for(int j=1; j<=cnt; ++j)
                    for(int k=j,nj=std::min(j,w[r]),nk; k<=cnt; ++k)
                        nk=std::max(k,w[r]), g[l][r][nj][nk]=std::min(g[l][r][nj][nk],g[l][i][j][k]+f[i+1][r-1]);
            for(int i=l-1; i<r; ++i)
                for(int j=1; j<=cnt; ++j)
                    for(int k=j; k<=cnt; ++k)
                        f[l][r]=std::min(f[l][r],f[l][i]+g[i+1][r][j][k]+A+B*Sqr(ref[k]-ref[j]));
//                      f[l][r]=std::min(f[l][r],g[l][i][j][k]+f[i+1][r]+A+B*Sqr(ref[k]-ref[j]));
        }
    printf("%d\n",f[1][n]);

    return 0;
}