• BZOJ 1564: [NOI2009]二叉查找树


    链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1564

    Description

    Input

    Output

    只有一个数字,即你所能得到的整棵树的访问代价与额外修改代价之和的最小值。

    Sample Input

    4 10
    1 2 3 4
    1 2 3 4
    1 2 3 4

    Sample Output

    29
    题解:我们还是老规矩设一个区间f[i][j][w]为区间i~j且树上的每个节点都大于等于w(因为子节点权值一定大于根节点,所以其实就是根节点要大于等于w),来形成一棵树的最小访问代价与额外代价之和。
    然后枚举一个k把区间分为左边和右边,k为这棵树的根节点,但是为了左儿子数据值小于根节点,k左边的数据值一定小于k,右边一定大于k,我们事先要把数据值从小到大排序,来保证枚举的区间符合条件。枚举k后我们就要判断一下,如果k节点的权值要大于w,那么我们就不用修改,只要把左边子树值加上右边子树的值再加上本身的访问代价即可,(可能会有点奇怪,我们怎么确保左边和右边的权值一定大于我们枚举的这个根节点呢?哈,这就是w的作用啦,左边的值为f[i][k-1][w],说明左边的所有节点都需大于等于w,如果不存在的话,就会是最大值,所以不会被计算进来),如果要修改再额外加上最初输入的k就可以了。还有一个比较重要的地方,我们需要枚举w,可是原题中的w过大,导致空间超限了,这里就需要进行一个离散化,为什么可以这么做呢?我们可以发现他保证了每个权值都不相同且我们判断时只需要利用他的大小关系,与他本身是无关的,所以可以在一开始的时候就排一下序,然后把权值都变小,就搞定了。
    具体的细节还是在程序中写吧。
    程序:
    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int n,j,k1,s[200],f[200][200][200];
    struct h
    {
        int x,y,z;
    }a[1000];
    bool cmp1(const h&a,const h&b)
    {
        return (a.y<b.y);
    }
    bool cmp2(const h&a,const h&b)
    {
        return (a.x<b.x);
    }
    int main()
    {
       scanf("%d %d",&n,&k1);
       for (int i=1;i<=n;i++) cin>>a[i].x;
       for (int i=1;i<=n;i++) cin>>a[i].y;
       for (int i=1;i<=n;i++) cin>>a[i].z;
       sort(a+1,a+1+n,cmp1);//按照权值排序
       for (int i=1;i<=n;i++) a[i].y=i;//进行离散化
       sort(a+1,a+1+n,cmp2);//为了符合条件再按照数据值排序
       for (int i=1;i<=n;i++)
       s[i]=s[i-1]+a[i].z;//用前缀和把访问频率记录下来,方便计算
       
       for (int i=0;i<=n+1;i++)
        for (int j=0;j<=n+1;j++)
         for (int w=1;w<=n+1;w++)
         f[i][j][w]=210000000;//初始化
         
       for (int i=1;i<=n+1;i++)  
       for (int w=0;w<=n;w++)
       f[i][i-1][w]=0;//当我们在一个区间中枚举边界i或j为根节点时,我们就会访问到某个f[i][i-1][w],
    但其实他是没有左子树或右子树的,所以我们就把它记为0,反正加上去了也没有任何影响。
    for (int i=1;i<=n;i++) for (int w=1;w<=n;w++) if (a[i].y>=w) f[i][i][w]=a[i].z;//把本身当做一棵树且这棵树的权值大于等于w时,不必修改。 else f[i][i][w]=a[i].z+k1;//否则就要加上修改的代价 for (int w=n;w>=1;w--) for (int len=2;len<=n;len++) for (int i=1;i<=n-len+1;i++)//枚举 { j=i+len-1; for (int k=i;k<=j;k++) { 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]+s[j]-s[i-1]);//不需修改时 f[i][j][w]=min(f[i][j][w],f[i][k-1][w]+f[k+1][j][w]+s[j]-s[i-1]+k1); //修改时 } } printf("%d",f[1][n][1]);//因为离散化后节点中权值最小的是1,所以根节点的权值一定是1. return 0; }
  • 相关阅读:
    通过组合mesh优化资源
    unity四元数
    Unity3D开发之Matrix4x4矩阵变换
    Unity用矩阵进行坐标转换
    【Unity3D的四种坐标系】
    unity里的向量
    Unity3D_场景の烘培
    本地电脑配ssh key的几个命令
    Unity两个Transparent/Diffuse渲染的半透明材质千万不要重叠呀
    new GameObject的巧妙用法
  • 原文地址:https://www.cnblogs.com/2014nhc/p/6247039.html
Copyright © 2020-2023  润新知