BZOJ.4897.[Thu Summer Camp2016]成績單(區間DP)
阿新 • • 發佈:2018-12-29
顯然是個區間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]\}\)
複雜度\(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; }