CVII.[NOI2009]二叉查找树
首先该树的中序遍历是唯一可以确定的(直接按照数据值排序即可)。
然后,因为权值可以被修改成一切实数,故我们完全可以把权值离散化掉。
于是我们现在可以设置一个DP状态\(f[l,r,lim]\)表示:
区间\([l,r]\)中的所有东西构成了一棵子树,且树中最小权值不小于\(lim\)的最优方案。
然后就枚举根转移即可。转移的时候就可以看作是子树内所有东西被整体提高了一层,所以直接增加\(sum[l,r]\)(意为区间\([l,r]\)中的所有数据值之和)即可。同时,如果有当前枚举的根的权值不小于\(lim\),显然就可以不修改,但是两边儿子的权值就必须比它大;否则则必须修改,两边儿子的权值下限还是\(lim\)(因为根的权值可以被修改成一个略大于\(lim\)的实数)。
则时间复杂度\(O(n^4)\)。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,sum[110],f[110][110][110];
struct dat{
int val,key,lam;
}a[100];
int dfs(int l,int r,int lim){
if(l>r)return 0;
if(f[l][r][lim]!=-1)return f[l][r][lim];
int &now=f[l][r][lim];now=0x3f3f3f3f;
for(int i=l;i<=r;i++){//assume that i is the root in the section [l,r].
if(a[i].key>=lim)now=min(now,dfs(l,i-1,a[i].key)+dfs(i+1,r,a[i].key)+sum[r]-sum[l-1]);//do not modify, the height simply increased by one.
now=min(now,dfs(l,i-1,lim)+dfs(i+1,r,lim)+m+sum[r]-sum[l-1]);//modify i to any real number a little greater than lim.
}
return now;
}
int main(){
scanf("%d%d",&n,&m),memset(f,-1,sizeof(f));
for(int i=1;i<=n;i++)scanf("%d",&a[i].val);
for(int i=1;i<=n;i++)scanf("%d",&a[i].key);
for(int i=1;i<=n;i++)scanf("%d",&a[i].lam);
sort(a+1,a+n+1,[](dat u,dat v){return u.key<v.key;});
for(int i=1;i<=n;i++)a[i].key=i;
sort(a+1,a+n+1,[](dat u,dat v){return u.val<v.val;});
for(int i=1;i<=n;i++)sum[i]=sum[i-1]+a[i].lam;
printf("%d\n",dfs(1,n,1));
return 0;
}