zoukankan      html  css  js  c++  java
  • 【NOI2009】二叉查找树

    题面

    https://www.luogu.org/problem/P1864

    题解

    首先离散化优先级。

    首先按权值排序,得到$dfs$序,一棵子树对于$dfs$序上一段区间,区间$dp$的经典模型。

    设$F[k][l][r]$为对于$[l..r]$的元素,它们形成一棵二叉查找树,并且根节点的优先级$>k$的最小代价。(这样就可以接上一个优先级为$k$的根了)

    转移时,枚举根节点(中间点),我们只考虑修改根节点的优先级,

    不用修改时($a[d].p>k$),有$$f[k][l][r]=min(f[k][l][r],f[a[d].p][l][d-1]+f[a[d].p][d+1][r]+sum[r]-sum[l-1])$$

    注意这里不能直接写成$f[k][l][r]=min(f[k][l][r],f[k][l][d-1]+f[k][d+1][r]+sum[r]-sum[l-1])$,因为这样写相当于不付任何代价的直接把$d$的优先级变大了,会对后面的值产生影响。

    正确的式子本质上是一个和之前求的取$min$的过程。

    需要修改时(一般情况)

    $$f[k][l][r]=min(f[k][l][r],f[k][l][d-1]+f[k][d+1][r]+sum[r]-sum[l-1]+kk)$$

    写这道题的时候我逻辑有些混乱,自己梳理一遍才行。

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define ri register int
    #define N 75
    using namespace std;
    
    inline int read() {
        int ret=0,f=0; char ch=getchar();
        while (ch<'0' || ch>'9') f|=(ch=='-'),ch=getchar();
        while (ch>='0' && ch<='9') ret*=10,ret+=ch-'0',ch=getchar();
        return f?-ret:ret;
    }
    
    struct node {
        int v,p,w;
        bool operator < (const node &rhs) const {
            return v<rhs.v;
        }
    } a[N];
    int n,kk,b[N],sum[N];
    int f[N][N][N];
    
    int main() {
      n=read(); kk=read();
      for (ri i=1;i<=n;i++) a[i].v=read();
      for (ri i=1;i<=n;i++) a[i].p=read();
      for (ri i=1;i<=n;i++) a[i].w=read();
      sort(a+1,a+n+1);
      for (ri i=1;i<=n;i++) b[i]=a[i].p;
      sort(b+1,b+n+1);
      int t=unique(b+1,b+n+1)-b-1;
      for (ri i=1;i<=n;i++) a[i].p=lower_bound(b+1,b+t+1,a[i].p)-b;
      for (ri i=1;i<=n;i++) sum[i]=sum[i-1]+a[i].w;
      memset(f,0x3f,sizeof(f));
      for (ri i=0;i<=n;i++) 
          for (ri k=0;k<=t;k++) f[k][i+1][i]=0;
      for (ri i=1;i<=n;i++)
          for (ri k=0;k<=t;k++) if (a[i].p>k) f[k][i][i]=a[i].w; else f[k][i][i]=a[i].w+kk;
    
      for (ri len=1;len<n;len++)
          for (ri l=1;l+len<=n;l++) {
              int r=l+len;
              for (ri k=t;k>=0;k--) { 
                  for (ri d=l;d<=r;d++) {
                      if (a[d].p>k) f[k][l][r]=min(f[k][l][r],f[a[d].p][l][d-1]+f[a[d].p][d+1][r]+sum[r]-sum[l-1]);
                      f[k][l][r]=min(f[k][l][r],f[k][l][d-1]+f[k][d+1][r]+sum[r]-sum[l-1]+kk);
                  }
              }
          }
      cout<<f[0][1][n]<<endl;
      return 0;
    }
  • 相关阅读:
    魔术方法___toString()
    魔术方法__set()
    魔术方法__get()
    php面向对象之final关键字用法及实例
    php面向对象之什么是抽象类?及抽象类的作用
    php面向对象之对象克隆方法
    php面向对象之对象比较用法详解
    php面向对象之instanceof关键字的用法
    php表单怎么提交到数据库?
    php表单的验证详解
  • 原文地址:https://www.cnblogs.com/shxnb666/p/11797063.html
Copyright © 2011-2022 走看看