二叉查找树
【题目描述】
已知一棵特殊的二叉查找树。根据定义,该二叉查找树中每个结点的数据值都比它左儿子结点的数据值大,而比它右儿子结点的数据值小。
另一方面,这棵查找树中每个结点都有一个权值,每个结点的权值都比它的儿子结点的权值要小。
已知树中所有结点的数据值各不相同;所有结点的权值也各不相同。这时可得出这样一个有趣的结论:如果能够确定树中每个结点的数据值和权值,那么树的形态便可以唯一确定。因为这样的一棵树可以看成是按照权值从小到大顺序插入结点所得到的、按照数据值排序的二叉查找树。
一个结点在树中的深度定义为它到树根的距离加1。因此树的根结点的深度为1。
每个结点除了数据值和权值以外,还有一个访问频度。我们定义一个结点在树中的访问代价为它的访问频度乘以它在树中的深度。整棵树的访问代价定义为所有结点在树中的访问代价之和。
现在给定每个结点的数据值、权值和访问频度,你可以根据需要修改某些结点的权值,但每次修改你会付出K的额外修改代价。你可以把结点的权值改为任何实数,但是修改后所有结点的权值必须仍保持互不相同。现在你要解决的问题是,整棵树的访问代价与额外修改代价的和最小是多少?
【输入格式】
输入文件中的第一行为两个正整数N,K。其中:N表示结点的个数,K表示每次修改所需的额外修改代价。
接下来的一行为N个非负整数,表示每个结点的数据值。
再接下来的一行为N个非负整数,表示每个结点的权值。
再接下来的一行为N个非负整数,表示每个结点的访问频度。
其中:所有的数据值、权值、访问频度均不超过400000。每两个数之间都有一个空格分隔,且行尾没有空格。
【输出格式】
输出文件中仅一行为一个数,即你所能得到的整棵树的访问代价与额外修改代价之和的最小值。
【样例输入】
4 10
1 2 3 4
1 2 3 4
1 2 3 4
【样例输出】
29
分析:
参加比赛遇到的题目,比赛时看到题目感觉是用树型dp,但一直没有思路,只好用近似模拟的方法乱搞的,后来看了题解才会做......
首先我们知道该树是一颗特殊的二叉排序树,它比普通的多一个优先级(在本题中就是权值),使它有了堆的性质。如果没有修改操作,我们可以根据这些性质构造一颗这样的树然后模拟,而本题的难度就在修改,对此可以用动规解决。
定义f[i,j,w]为第i..j的节点构成节点权值大于w的子树遍历的最小代价,a[i,1]保存节点的数据值,a[i,2]保存节点离散化后的权值,a[i,3]保存节点的访问频度,s[i,j]为第i..j的节点访问频度之和。
离散化权值,就是将权值按从小到大排序后用其在数组中编号来表示其权值,之所以能这样做是因为节点权值各部相同,而每个点权值数字本身多少没有意义,重要的是它与其它节点权值相比的大小关系,离散化后就可以将大小参差不齐的数字按大小的关系转换为有序的编号,可以节省空间,也可动规中的一些麻烦的东西。
动规式子分两种情况
在i..j中枚举根节点k
若不修改,条件是k的权值>=w,则f[i,j,w]:=max(f[i,j,w],f[i,k-1,a[k,2]]+f[k+1,j,a[k,2]]+s[i,j])
若修改,则f[i,j,w]:=max(f[i,j,w],f[i,k-1,w]+f[k+1,j,w]+s[i,j]+k)
解释:如果不修改,k必须在子树中,其左右孩子权值均要大于k的权值;如果将k修改为根节点,其左右孩子权值可比w大很小很小的一个数字。
代码
program treap; var f:array[0..100,0..100,0..100]of longint; a:array[1..3,0..100]of longint; s:array[0..100,0..100]of longint; n,i,m,j,w,k:longint; function min(x,y:longint):longint; begin if x<y then min:=x else min:=y; end; procedure sort(x:longint); var i,j,t:longint; begin for i:=1 to n-1 do for j:=i+1 to n do if a[x,i]>a[x,j] then begin t:=a[1,i]; a[1,i]:=a[1,j]; a[1,j]:=t; t:=a[2,i]; a[2,i]:=a[2,j]; a[2,j]:=t; t:=a[3,i]; a[3,i]:=a[3,j]; a[3,j]:=t; end; end; begin assign(input,‘treap.in‘); reset(input); assign(output,‘treap.out‘); rewrite(output); readln(n,m); for i:=1 to n do read(a[1,i]); readln; for i:=1 to n do read(a[2,i]); readln; for i:=1 to n do read(a[3,i]); sort(2); for i:=1 to n do a[2,i]:=i; sort(1); for i:=1 to n do for j:=i to n do s[i,j]:=s[i,j-1]+a[3,j]; for i:=1 to n do for j:=1 to n do for w:=1 to n do f[i,j,w]:=maxlongint div 10; for i:=1 to n+1 do for w:=0 to n do f[i,i-1,w]:=0; for w:=n downto 1 do for i:=n downto 1 do for j:=i to n do for k:=i to j do if i=j then if a[2,k]>=w then f[i,j,w]:=a[3,k] else f[i,j,w]:=a[3,k]+m else begin if a[2,k]>=w then f[i,j,w]:=min(f[i,j,w],f[i,k-1,a[2,k]]+f[k+1,j,a[2,k]]+s[i,j]); f[i,j,w]:=min(f[i,j,w],f[i,k-1,w]+f[k+1,j,w]+s[i,j]+m); end; writeln(f[1,n,1]); close(input); close(output); end.