自己的思维能力果然还是太不够……想到了这棵树所有的性质即中序遍历不变,却并没有想到怎样利用这一点。在想这道题的过程中走入了诸多的误区,在这里想记录一下 & 从中吸取到的教训(原该可以避免的吧)。

  1. 注意到了中序遍历不变的性质却不会使用。

  2. 注意到只有相对大小才会影响树的形态,在考虑的时候一直在想如何改变一个数的位置关系,分析修改权值对于树产生的影响。但这样是很难分析的,将树看作一个整体也不利于dp状态的划分。

  3. 想不出怎样计算一个节点的深度(不能快速找到一个节点所处的位置)。

  正确的思维应当是:

  1.中序遍历不变 ---> 数值排名位于 [l, r] 这个区间中的所有树必然可能同在一棵子树中(且这棵子树中不含其他的节点);

  2.不好计算一个节点插入的深度 / 不好修改一个数在子树中的位置:改修改为插入。整棵子树中,唯一一个容易确定位置的节点:若这个节点是当前子树中权值最小的,则这个节点为子树的根,其余所有节点的深度 ++;

  于是正确的 dp 状态就出来了。\(dp[l][r][v]\) 表示 \(l\) 到 \(r\) 的区间所有节点 \( >= v \) 的最小代价。转移方程分别为两种:修改该节点权值 & 不修改该节点权值。

    \(dp[l][r][v] = dp[l][k - 1][v] + dp[k + 1][r][v] + K + sum[l][r];\)

    \(dp[l][r][v] = dp[l][k - 1][P[i].v] + dp[k + 1][r][P[i].v] + sum[l][r];\)

  其中,第二个转移方程中要求 \(P[i].v >= v\),\(P[i].v\) 是节点原本的权值,\(sum[l][r]\) 为区间 \(l -> r\) 的访问频率。

#include <bits/stdc++.h>
using namespace std;
#define maxn 100
#define int long long
#define INF 999999999999LL
int n, K, sum[maxn];
int ans = INF, dp[maxn][maxn][maxn]; struct node
{
int num, v, w;
}P[maxn]; int read()
{
int x = , k = ;
char c;
c = getchar();
while(c < '' || c > '') { if(c == '-') k = -; c = getchar(); }
while(c >= '' && c <= '') x = x * + c - '', c = getchar();
return x * k;
} bool cmp(node a, node b) { return a.v < b.v; }
bool cmp1(node a, node b) { return a.num < b.num; }
void gmin(int &x, int y) { x = x < y ? x : y; } int dfs(int l, int r, int v)
{
if(~dp[l][r][v]) return dp[l][r][v];
else dp[l][r][v] = INF;
if(l > r) return dp[l][r][v] = ;
for(int k = l; k <= r; k ++)
{
gmin(dp[l][r][v], dfs(l, k - , v) + dfs(k + , r, v) + K + sum[r] - sum[l - ]);
if(P[k].v >= v) gmin(dp[l][r][v], dfs(l, k - , P[k].v) + dfs(k + , r, P[k].v) + sum[r] - sum[l - ]);
}
return dp[l][r][v];
} signed main()
{
n = read(), K = read();
memset(dp, -, sizeof(dp));
for(int i = ; i <= n; i ++) P[i].num = read();
for(int i = ; i <= n; i ++) P[i].v = read();
for(int i = ; i <= n; i ++) P[i].w = read();
sort(P + , P + + n, cmp);
for(int i = ; i <= n; i ++) P[i].v = i;
sort(P + , P + + n, cmp1);
for(int i = ; i <= n; i ++) sum[i] += sum[i - ] + P[i].w;
ans = min(ans, dfs(, n, ));
printf("%lld\n", ans);
return ;
}

  2. 加分二叉树

  双倍经验……

#include <bits/stdc++.h>
using namespace std;
#define maxn 40
#define int long long
int n, a[maxn];
int f[maxn][maxn], dp[maxn][maxn]; int read()
{
int x = , k = ;
char c;
c = getchar();
while(c < '' || c > '') { if(c == '-') k = -; c = getchar(); }
while(c >= '' && c <= '') x = x * + c - '', c = getchar();
return x * k;
} int dfs(int l, int r)
{
if(dp[l][r]) return dp[l][r];
if(l > r) return dp[l][r] = ;
if(l == r) return f[l][r] = l, dp[l][r] = a[l];
for(int k = l; k <= r; k ++)
{
int x = dfs(l, k - ), y = dfs(k + , r);
if(dp[l][r] < x * y + a[k])
{
dp[l][r] = x * y + a[k];
f[l][r] = k;
}
}
return dp[l][r];
} void Get_ans(int l, int r)
{
if(l > r) return;
printf("%lld ", f[l][r]);
Get_ans(l, f[l][r] - ), Get_ans(f[l][r] + , r);
} signed main()
{
n = read();
for(int i = ; i <= n; i ++) a[i] = read();
printf("%lld\n", dfs(, n));
Get_ans(, n); puts("");
return ;
}

【题解】NOI2009二叉查找树 + NOIP2003加分二叉树的更多相关文章

  1. cogs 106. [NOIP2003] 加分二叉树(区间DP)

    106. [NOIP2003] 加分二叉树 ★☆   输入文件:jfecs.in   输出文件:jfecs.out   简单对比时间限制:1 s   内存限制:128 MB [问题描述] 设 一个 n ...

  2. NOIP2003加分二叉树[树 区间DP]

    题目描述 设一个n个节点的二叉树tree的中序遍历为(1,2,3,…,n),其中数字1,2,3,…,n为节点编号.每个节点都有一个分数(均为正整数),记第i个节点的分数为di,tree及它的每个子树都 ...

  3. NOIP2003 加分二叉树

    http://www.luogu.org/problem/show?pid=1040 题目描述 设一个n个节点的二叉树tree的中序遍历为(1,2,3,…,n),其中数字1,2,3,…,n为节点编号. ...

  4. NOIP-2003 加分二叉树

    题目描述 设一个n个节点的二叉树tree的中序遍历为(1,2,3,…,n),其中数字1,2,3,…,n为节点编号.每个节点都有一个分数(均为正整数),记第i个节点的分数为di,tree及它的每个子树都 ...

  5. NOIP2003加分二叉树

    题目描述 设一个n个节点的二叉树tree的中序遍历为(1,2,3,…,n),其中数字1,2,3,…,n为节点编号.每个节点都有一个分数(均为正整数),记第ii个节点的分数为di,treedi,tree ...

  6. CJOJ 1010【NOIP2003】加分二叉树 / Luogu 1040 加分二叉树(树型动态规划)

    CJOJ 1010[NOIP2003]加分二叉树 / Luogu 1040 加分二叉树(树型动态规划) Description 设 一个 n 个节点的二叉树 tree 的中序遍历为( 1,2,3,-, ...

  7. 加分二叉树 vijos1991 NOIP2003第三题 区间DP/树形DP/记忆化搜索

    描述 设一个n个节点的二叉树tree的中序遍历为(l,2,3,-,n),其中数字1,2,3,-,n为节点编号.每个节点都有一个分数(均为正整数),记第i个节点的分数为di,tree及它的每个子树都有一 ...

  8. 【算法•日更•第十期】树型动态规划&区间动态规划:加分二叉树题解

    废话不多说,直接上题: 1580:加分二叉树 时间限制: 1000 ms         内存限制: 524288 KB提交数: 121     通过数: 91 [题目描述] 原题来自:NOIP 20 ...

  9. CODEVS1090 加分二叉树

    codevs1090 加分二叉树 2003年NOIP全国联赛提高组 题目描述 Description 设一个n个节点的二叉树tree的中序遍历为(l,2,3,…,n),其中数字1,2,3,…,n为节点 ...

随机推荐

  1. IDEA中解决Edit Configurations中没有tomcat Server选项的问题(附配置Tomcat)

    1.点击File-->settings(Ctrl+Alt+S) 2.在弹出的窗口中的搜索框中输入appliation,然后选择下方的Plugins,再然后勾选左侧Installed中的如图所示的 ...

  2. mysql数据库和数据表的简单操作

    一.数据库的增删改查 1.新建数据库 CREATE DATABASE 数据库名 charset utf8; 数据库名规则:可以由字母.数字.下划线.@.#.$ 区分大小写, 不能使用关键字如 crea ...

  3. css3新样式

    超出两行变省略号 overflow:hidden; text-overflow:ellipsis;display:-webkit-box; -webkit-box-orient:vertical;-w ...

  4. HBase学习(三):数据模型

    和传统的关系型数据库类似,HBase以表(Table)的方式组织数据.HBase的表由行(Row)和列(Column)共同构成,与关系型数据库不同的是HBase有一个列族(ColumnFamily)的 ...

  5. 数据分析处理库Pandas——字符串操作

    字符串小写 字符串大写 字符串长度 去掉字符串中的空格 去掉字符串中的左空格 去掉字符串中的右空格 字符串替换 按字符串切割 字符串是否包含在另一个字符串中

  6. centos编译安装rabbitmq

    安装环境 [root@VM_12_50_centos rabbitmq]# uname -a Linux VM_12_50_centos 3.10.0-514.21.1.el7.x86_64 #1 S ...

  7. webug学习(1)

    webug的题目,比较简单,拿来巩固一哈. 1. 一看就知道是注入漏洞了,啥也不说sqlmap直接开炮. 先-u 之后-u 网址 --current-db 获取当前网址的数据库 所以当前数据库就是 p ...

  8. python基础——列表、字典

    Python核心数据类型--列表 列表是一个任意类型的对象的位置相关的有序集合,它没有固定的大小.大小可变的,通过偏移量进行赋值以及其他各种列表的方法进行调用,能够修改列表.其他更多的功能可以查阅py ...

  9. 怎么防止别人动态在你程序生成代码(怎么防止别人反编译你的app)

    1.本地数据加密 iOS应用防反编译加密技术之一:对NSUserDefaults,sqlite存储文件数据加密,保护帐号和关键信息 2.URL编码加密 iOS应用防反编译加密技术之二:对程序中出现的U ...

  10. 自动化测试--封装JDBCUnit

    在进行测试的时候,经常需要对数据库进行操作.我们知道,通过代码与数据库交互,需要以下几步: 1.加载驱动 之前有盆友问我,为什么Selenium操作浏览器的时候,非要下载浏览器驱动?为啥对数据库进行操 ...