[NOI2009]二叉查找树

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;
}

posted @ 2021-03-31 14:18  Troverld  阅读(60)  评论(0编辑  收藏  举报