题解 P1864 【[NOI2009]二叉查找树】

#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#define rint register int
#define min(a,b) (a<b? a:b)
using namespace std;
struct node{int x,y,z;}a[100];
int f[100][100][100];
int n,K;
inline int read()
{
    int w=1,ans=0;
    char ch=getchar();
    while(!isdigit(ch)) {if (ch=='-') w=-1; ch=getchar();}
    while(isdigit(ch)) {ans=(ans<<3)+(ans<<1)+(ch^48); ch=getchar();}
    return ans*w;
}
inline bool cmp(node a,node b)
{
    return a.y<b.y;
}
inline bool cmp2(node a,node b)
{
    return a.x<b.x;
}
int main()
{
    //freopen("tree.in","r",stdin);
    //freopen("tree.out","w",stdout);
    n=read();K=read();
    for (int i=1;i<=n;i++) a[i].x=read();//数据
    for (int i=1;i<=n;i++) a[i].y=read();//权值
    for (int i=1;i<=n;i++) a[i].z=read();//访问频度
    sort(a+1,a+n+1,cmp);//按权值排序,离散化
    for (int i=1;i<=n;i++) a[i].y=i;//将权值离散化
    sort(a+1,a+n+1,cmp2);
    for (int i=1;i<=n;i++) a[i].z+=a[i-1].z;//前缀和 
    memset(f,0x7f,sizeof f);
    for (int i=1;i<=n+1;i++)
     for (int w=0;w<=n;w++)
      f[i][i-1][w]=0;
      
    for (rint w=n;~w;w--)//~w指w非零,区间内所有权值大于w 
     for (rint i=n;i;i--)
      for (rint j=i;j<=n;j++)
       for (rint k=i;k<=j;k++)//区间dp枚举根节点位置
       {
         if (a[k].y>=w) 
          f[i][j][w]=min(f[i][j][w],f[i][k-1][a[k].y]+f[k+1][j][a[k].y]+a[j].z-a[i-1].z);
          //a[j].z-a[i-1].z:当树深加1,把每个访问频度都再加一次 
         f[i][j][w]=min(f[i][j][w],f[i][k-1][w]+f[k+1][j][w]+a[j].z-a[i-1].z+K);
       }
    printf("%d",f[1][n][1]);
    return 0;    
}
posted @ 2019-07-16 14:44  Randolph、  阅读(149)  评论(0编辑  收藏  举报