LOJ 2292 「THUSC 2016」成绩单——区间DP

时间:2019-04-19
本文章向大家介绍LOJ 2292 「THUSC 2016」成绩单——区间DP,主要包括LOJ 2292 「THUSC 2016」成绩单——区间DP使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

题目:https://loj.ac/problem/2292

直接 DP 很难做,主要是有那种 “一个区间内部有很多个别的区间” 的情况。

自己想了一番枚举 max-min 的最大限制,然后在该基础上最小化区间个数之类的。还是不会。

看了题解才会。

考虑再设一个 dp 数组来辅助表示那种麻烦的情况。

值可以离散化!又因为代价与值有关,可以考虑把值放进角标里。

令 f[ i ][ j ] 表示把 [ i , j ] 全取完的最小代价,g[ i ][ j ][ l ][ r ] 表示把 [ i , j ] 取得只剩下值在 [ l , r ] 之间的最小代价。

g[ i ][ j ][ l ][ r ] 转移时讨论一下 j 是否留下。若留下,则从 g[ i ][ j-1 ][ l ][ r ] 转移,否则枚举和 j 一起删掉的区间,从 g[ i ][ k-1 ][ l ][ r ] + f[ k ][ j ] 转移。

然后 f[ i ][ j ] 就是各种 g[ i ][ j ][ l ][ r ] 再加上把值在 [ l , r ] 的数一次取完的代价。

这种设状态为 “做到剩下特定元素” 的思想很好。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int Mx(int a,int b){return a>b?a:b;}
int Mn(int a,int b){return a<b?a:b;}
const int N=55,INF=1005;
int n,m,A,B,a[N],tp[N],f[N][N],g[N][N][N][N];
int Sqr(int x){return x*x;}
void cz(int &u,int v){if(v<u)u=v;}
int main()
{
  scanf("%d%d%d",&n,&A,&B);
  for(int i=1;i<=n;i++)scanf("%d",&a[i]),tp[i]=a[i];
  sort(tp+1,tp+n+1); m=unique(tp+1,tp+n+1)-tp-1;
  for(int i=1;i<=n;i++)a[i]=lower_bound(tp+1,tp+m+1,a[i])-tp;
  memset(g,0x3f,sizeof g);
  for(int i=1;i<=n;i++)
    {
      f[i][i]=A;
      for(int l=1;l<=m;l++)
    for(int r=l;r<=m;r++)
      {
        if(a[i]>=l&&a[i]<=r)g[i][i][l][r]=0;
        else g[i][i][l][r]=A;
      }
    }
  for(int d=1;d<n;d++)
    for(int i=1;i+d<=n;i++)
      {
    int j=i+d; int mx=0,mn=INF;
    for(int k=i;k<=j;k++)
      mx=Mx(mx,a[k]),mn=Mn(mn,a[k]);
    f[i][j]=A+B*Sqr(tp[mx]-tp[mn]);
    for(int l=1;l<=m;l++)
      for(int r=l;r<=m;r++)
        {
          if(a[j]>=l&&a[j]<=r)
        cz(g[i][j][l][r],g[i][j-1][l][r]);
          for(int k=i+1;k<=j;k++)
        cz(g[i][j][l][r],g[i][k-1][l][r]+f[k][j]);
          cz(f[i][j],g[i][j][l][r]+A+B*Sqr(tp[r]-tp[l]));
        }
      }
  printf("%d\n",f[1][n]);
  return 0;
}