bzoj1564 二叉查找树

Description

Input

Output

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

Sample Input

4 10

1 2 3 4

1 2 3 4

1 2 3 4

Sample Output

29

HINT

输入的原图是左图,它的访问代价是1×1+2×2+3×3+4×4=30。最佳的修改方案是把输入中的第3个结点的权值改成0,得到右图,访问代价是1×2+2×3+3×1+4×2=19,加上额外修改代价10,一共是29。

Source

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

这一道题我在听jiry上课时听过,但是却没有一遍过。

事实上,我交了6遍RE才变成WA,然后又交了4遍WA才A了。。。。。。。

不是我水平不行(其实是的),NOI的题太难了。

我们切入正题,二叉查找树有两种方法可以解决,一种是数据结构,然而对于我们这种弱菜。。。。。

另一种叫树形DP,勉强可以接受了。

第一步:将权值按从小到大排序后用其在数组中编号来表示其权值

第二步:

既然是动态规划,方程是要列的

首先我们对所有点的权值进行离散化,先前已经做过了,这样每个点的权值必然在区间[1,n] 中,用f[i][j][w] 来表示前序遍历中,区间[i,j] 对应的点作为一个子树,并且子树中所有权值都大于等于w ,这个子树访问代价与修改代价之和的最小值。

显然我们要枚举这个子树的根节点k ,那么有两种情况:1、点k 本来的权值是小于w 的,那么要符合小根堆的特性,点k 权值要修改为w 。2、点k 本来的权值就是大于等于w 的,那么可以选择不修改点k 的权值,也可以选择修改点k 的权值。

然后得到方程

f[i][j][w]=min i≤k≤j {f[i][k?1][w]+f[k+1][j][w]+K+∑ t=i j frequence t },weight k <w

f[i][j][w]=min i≤k≤j {f[i][k?1][weight k ]+f[k+1][j][weight k ]+∑ t=i j frequence t }

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <string.h>
 5 #include <algorithm>
 6
 7 #define MAXN 80
 8
 9 using namespace std;
10
11 struct Node
12 {
13     int val; //键值
14     int weight; //权值
15     int frequence; //频率
16 }node[MAXN];
17
18 int n,K;
19
20 bool cmp(Node a,Node b)
21 {
22     return a.val<b.val;
23 }
24
25 int f[MAXN][MAXN][MAXN],stack[MAXN],top=0; //边界:f[i][i-1][w]=0,表示点i为根结点,下面无左儿子(i-1为根节点,下面无右儿子),显然f值为0
26 int sum[MAXN]; //频率的前缀和
27
28 int main()
29 {
30     scanf("%d%d",&n,&K);
31     for(int i=1;i<=n;i++)
32         scanf("%d",&node[i].val);
33     for(int i=1;i<=n;i++)
34         scanf("%d",&node[i].weight),stack[++top]=node[i].weight;
35     for(int i=1;i<=n;i++)
36         scanf("%d",&node[i].frequence);
37     sort(stack+1,stack+top+1);
38     for(int i=1;i<=n;i++)
39         node[i].weight=lower_bound(stack+1,stack+top+1,node[i].weight)-stack;
40     sort(node+1,node+n+1,cmp);
41     for(int i=1;i<=n;i++)
42         sum[i]=sum[i-1]+node[i].frequence;
43     memset(f,0x3f,sizeof(f));
44     for(int i=1;i<=n+1;i++)
45         for(int w=0;w<=n;w++)
46             f[i][i-1][w]=0;
47     for(int w=n;w>=1;w--)
48         for(int i=n;i>=1;i--)
49             for(int j=i;j<=n;j++)
50                 for(int k=i;k<=j;k++) //枚举[i,j]这段区间的点对应的子树,该子树以点k作为根节点
51                 {
52                     f[i][j][w]=min(f[i][j][w],f[i][k-1][w]+f[k+1][j][w]+K+sum[j]-sum[i-1]); //k本来权值小于K,将其权值变为k
53                     if(node[k].weight>=w) f[i][j][w]=min(f[i][j][w],f[i][k-1][node[k].weight]+f[k+1][j][node[k].weight]+sum[j]-sum[i-1]); //k的权值本身就大于等于K,因此可以不变
54                 }
55     int ans=0x3f3f3f3f;
56     for(int w=0;w<=n;w++)
57         ans=min(ans,f[1][n][w]);
58     printf("%d\n",ans);
59     return 0;
60 }

PS:其实这个方程我也没列出来,最后看题解看了好久才看懂

时间: 2024-12-21 23:51:55

bzoj1564 二叉查找树的相关文章

【树型DP】BZOJ1564 二叉查找树(noi2009)

二叉查找树 [题目描述] 已知一棵特殊的二叉查找树.根据定义,该二叉查找树中每个结点的数据值都比它左儿子结点的数据值大,而比它右儿子结点的数据值小. 另一方面,这棵查找树中每个结点都有一个权值,每个结点的权值都比它的儿子结点的权值要小. 已知树中所有结点的数据值各不相同:所有结点的权值也各不相同.这时可得出这样一个有趣的结论:如果能够确定树中每个结点的数据值和权值,那么树的形态便可以唯一确定.因为这样的一棵树可以看成是按照权值从小到大顺序插入结点所得到的.按照数据值排序的二叉查找树. 一个结点在

bzoj1564: [NOI2009]二叉查找树

dp. 首先这棵树是一个treap. 权值我们可以改成任意实数,所以权值只表示相互之间的大小关系,可以离散化. 树的中序遍历是肯定确定的. 用f[l][r][w]表示中序遍历为l到r,根的权值必须大于w的最小代价. 当a[x].w<=w时有f[l][r][w]=min(f[l][x-1][w]+f[x+1][r][w]+s[l][r]+k).s[i][j]表示从l到r访问次数的和. 当a[x].w>w时,还有f[l][r][w]=min(f[l][x-1][w]+f[x+1][r][w]+s[

[BZOJ1564][NOI2009]二叉查找树(区间DP)

题目:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1564 分析: 首先因为每个点的数据值不变,所以无论树的形态如何变,树的中序遍历肯定不变,就是所有数据值从小到大排. 然后设f[i][j][v]表示中序遍历的i~j位组成一颗子树,其中要求权值都>=v的最小花费 枚举k作为树根(i<=k<=j) 则两种情况: 若k的权值<v,则f[i][j][v]=min(f[i][k-1][v]+f[k+1][j][v]+额外费用+i~

C语言实现二叉查找树

#include<stdio.h> #include<stdlib.h> /* * 数据结构:二叉查找树,即左孩子<父节点<右孩子 * C语言实现 * 2015-9-13 */ typedef struct TreeNode *PtrToNode; typedef PtrToNode Tree; typedef PtrToNode Position; struct TreeNode { int Element; Tree Left; //节点左孩子 Tree Right

按层遍历二叉查找树

<算法>中二叉查找树一节的习题:按层遍历二叉查找树. 可以使用队列来管理二叉查找树中的节点,节点按照如下方法入队出队: 节点x入队 当队列不为空时使用队列的第一个元素first 如果节点first.left不为空则将fisrt.left入队 如果节点first.right不为空则将first.right入队 将first节点出队 /** * Created by elvalad on 2014/12/5. * 按层遍历二叉查找树 */ import java.util.Iterator; im

二叉查找树BST 模板

二叉查找树BST 就是二叉搜索树 二叉排序树. 就是满足 左儿子<父节点<右儿子 的一颗树,插入和查询复杂度最好情况都是logN的,写起来很简单. 根据BST的性质可以很好的解决这些东西 1.查询值 int Search(int k,int x) { if(x<a[k].key && a[k].l) Search(a[k].l,x); else if(x>a[k].key && a[k].r) Search(a[k].r,x); else retur

二叉查找树

二叉查找(搜索)树(Binary Search Tree)又称二叉排序树(Binary Sort Tree),是基于二叉树,BST具有下列性质:1.若左子树不空,则其左子树上的所有结点的值均小于根结点的值:2.若右子树不空,则其右子树上的所有结点的值均大于根结点的值:3.左.右子树也分别为二叉查找树. 结点类 public class BinaryNode {      Integer data;      BinaryNode leftChild;      BinaryNode rightC

平衡树初阶——AVL平衡二叉查找树+三大平衡树(Treap + Splay + SBT)模板【超详解】

平衡树初阶——AVL平衡二叉查找树 一.什么是二叉树 1. 什么是树. 计算机科学里面的树本质是一个树状图.树首先是一个有向无环图,由根节点指向子结点.但是不严格的说,我们也研究无向树.所谓无向树就是将有向树的所有边看成无向边形成的树状图.树是一种递归的数据结构,所以我们研究树也是按照递归的方式去研究的. 2.什么是二叉树. 我们给出二叉树的递归定义如下: (1)空树是一个二叉树. (2)单个节点是一个二叉树. (3)如果一棵树中,以它的左右子节点为根形成的子树都是二叉树,那么这棵树本身也是二叉

java 二叉查找树

//接口+抽象类+实现类package wangChaoPA实习工作练习.com.进阶篇.二叉查找树; import java.util.Iterator;public interface Tree<E extends Comparable<E>>{    // 从树中删除e    boolean delete(E e); // 树的大小    int getSize(); // 中序遍历树    void inorder(); // 把e插入到tree中    boolean i