POJ 2057 The Lost House [树状DP]
题意:一只蜗牛将壳忘在了一棵树的某一个末结点(叶子)上。它想找回自己的壳,但忘记是丢在哪个结点上了,只好从树根开始网上爬,一个结点一个结点地找。在一些结点上居住着毛毛虫,它们会告诉蜗牛该结点以及它的子树上是否有它的壳,这样可以节省些时间。两个结点如果相连,则距离为1。问蜗牛找到壳的最小期望距离为多少。

接着给了一个例子(上图):
假如蜗牛以结点2,4,5的顺序找,则壳分别在结点2,4,5时它需要爬行的距离为1,4,6。则它找到壳的期望为(1 + 4 + 6) / 3 = 11/3。
考虑另一种情况,假设蜗牛先往结点3的方向爬,而在结点3处有毛毛虫,它可以告诉毛毛虫有关壳的信息,如果壳不在结点3和它的子树上,蜗牛就可以直接调转方向去2了。则以结点4,5,2的遍历顺序来考虑,它需要爬行的距离分别为2, 4, 3,则期望为(2 + 4 + 3) / 3 = 3。该例子中3即为最小期望距离。
思路:
该题真的很锻炼思维呀,脑细胞死了不少。。
首先需要用一个leaf[]数组来存储某节点子树上的叶子数,如上图,leaf[1] = 3, leaf[3] = 2。
此外,对于某结点x,壳只可能有两种状态:在或者不在结点x的子树上。因此,用success[x]表示当壳在结点x的子树上时蜗牛在子树每个叶子上分别找到壳的爬行距离(距离0是从结点x开始算起)之和,如上图,success[3] = 1 + 3;当先访问结点3时,success[1] = 2 + 4 + 3。 这里与上面例子的期望的计算过程对比可以看出,当success表示的值总为最优情况下的距离时,最终期望即为success[1] / leaf[1]。当x为叶子结点时,success[x] = 0。
当然,我们还要考虑在结点x的子树上找不到壳的情况,用fail[x]表示蜗牛在子树每个叶子上分别没有找到壳时的爬行距离(距离0是从结点x开始算起)之和,该值也应是最优的。最优体现在:如果x处有一只毛毛虫,而且提示x的子树上没有壳时,或者x结点为叶子时,蜗牛便不会再继续前进了,此时fail[x] = 0;否则,蜗牛就只好往前摸索了,此时fail[x] = sum(fail[y] + 2)。其中y是x的儿子结点。还是以上图为例,对于结点3来说,假如此处没有毛毛虫且壳不在3的子树上时,fail[3] = (fail[4] + 2) + (fail[5] + 2)。每个结点都要加2,是因为还要算上折返的距离。比如对于fail[4],蜗牛还要爬过边<3, 4>和<4, 3>,其他结点同理。
问题的重点是,如何得出每一个点的success值。
依然是先从简单的开始考虑,先不考虑让success最优。对于结点x,先将它的儿子结点按照遍历顺序从1开始编号,假设目前要遍历的为结点i,则结点1到(i-1)都已经遍历过了,且它们的子树上都没有壳。这里在计算success[x]的同时也在计算fail[x]。每遍历完一个儿子结点y时就有 fail[x] += fail[y] + 2。则当目前要遍历的结点为i时,fail[x]中恰好存储的是访问过结点1到(i-1)且都没有找到壳时的总距离。此时递推公式就比较好理解了:
success[x] += (fail[x] + 1) * leaf[i] + success[i]。
需要理解的是,success[i]表示的为结点i子树上找到壳的总距离,该距离是从结点i开始算起的。如果想将该距离转化为以结点x为起点,则需要加上在访问结点i之前走过的总路程,该总路程即为fail[x] + 1。蜗牛找完结点i-1后回到了x,然后从x爬向结点i,这里的加1即为边<x, i>的长度。此外,还需要考虑到结点i的子树上有不止一片叶子,则对于每一片叶子,在访问i之前都爬过了fail[x] + 1的距离,所以需要增加的总距离为(fail[x] + 1) * leaf[i]。
现在,将最优的前提也考虑进去,则对于结点x,其儿子的访问顺序将会决定是否最优。
假设x的其中两个儿子为y1, y2。
如果先y1后y2的顺序遍历到y2,此时有:
Δsuccess1 = (fail[x] + 1) * leaf[y1] + success[y1] + (fail[x] + fail[y1] + 2 + 1) * leaf[y2] + success[y2];
如果先y2后y1的顺序遍历到y1,此时有:
Δsuccess2 = (fail[x] + 1) * leaf[y2] + success[y2] + (fail[x] + fail[y2] + 2 + 1) * leaf[y1] + success[y1];
Δsuccess1 - Δsuccess2 = (fail[y1] + 2) * leaf[y2] - (fail[y2] + 2) * leaf[y1]。
这里用到了贪心,对于结点x,如果想让success[x]为最优,则每次遍历时取的儿子结点都应让success[x]的增量为最小。实现的方法是,当访问到结点x时,先用sort函数对x的所有儿子结点排序,排序的规则就是上面那个增量差值的公式,增量小的在前,然后依次遍历,这样得到的success[x]即为最优。
写了这么多,自己都觉得有些絮叨。这两天脑子昏昏沉沉的,这道题找别人题解看了两天,才渐渐想明白,因此就在这里一点点地分析思路,也是为了帮助向我一样做这题怎么想都想不明白的人吧。
此外,可能看上面的分析觉得很复杂,但如果看看代码,会觉得简单且好理解不少。有时候看文字解释,真不如读两行代码来得明白。
#include<stdio.h>
#include<string.h>
#include<vector>
#include<algorithm>
#include<iostream>
#define maxn 1005
using namespace std; int su[maxn], fail[maxn], le[maxn], n, root;
bool w[maxn];
vector<int> a[maxn];
bool cmp(int x,int y)
{
return (fail[x] + ) * le[y] < (fail[y] + ) * le[x];
}
void dp(int x)
{
if (a[x].empty())
{
le[x] = ;
return;
}
for (int i = ; i < a[x].size(); i++)
{
int y = a[x][i];
dp(y);
le[x] += le[y];
}
sort(a[x].begin(), a[x].end(), cmp);
for (int i = ; i < a[x].size(); i++)
{
int y = a[x][i];
su[x] += (fail[x] + ) * le[y] + su[y];
fail[x] += fail[y] + ;
}
if (w[x]) fail[x] = ;
}
int main()
{
//freopen("data.in", "r", stdin);
while (~scanf("%d", &n) && n)
{
memset(fail, , sizeof(fail));
memset(su, , sizeof(su));
memset(le, , sizeof(le));
memset(w, , sizeof(w));
for (int i = ; i <= n; i++)
a[i].clear();
for (int i = ; i <= n; i++)
{
int j;
char ch;
scanf("%d %c", &j, &ch);
if (j == -) root = i;
else a[j].push_back(i);
w[i] = (ch == 'Y' ? : );//w[i]表示该结点是否有毛毛虫
}
dp(root);
printf("%.4lf\n", . * su[root] / le[root]);
}
return ;
}
POJ 2057 The Lost House [树状DP]的更多相关文章
- poj 2342 Anniversary party_经典树状dp
题意:Ural大学有n个职员,1~N编号,他们有从属关系,就是说他们关系就像一棵树,父节点就是子节点的直接上司,每个职员有一个快乐指数,现在要开会,职员和职员的直接上司不能同时开会,问怎才能使开会的快 ...
- POJ 1655 Balancing Act (树状dp入门)
Description Consider a tree T with N (1 <= N <= 20,000) nodes numbered 1...N. Deleting any nod ...
- 树状DP (poj 2342)
题目:Anniversary party 题意:给出N各节点的快乐指数,以及父子关系,求最大快乐指数和(没人职员愿意跟直接上司一起玩): 思路:从底向上的树状DP: 第一种情况:第i个员工不参与,F[ ...
- poj3659树状DP
Cell Phone Network Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 6273 Accepted: 225 ...
- hdu 1561 The more, The Better_树状dp
题目链接 题意:给你一棵树,各个节点都有价值(除根节点),从根节点出发,选择m个节点,问最多的价值是多小. 思路:很明显是树状dp,遍历树时背包最优价值,dp[i][k]=max{dp[i][r]+d ...
- 树状DP HDU1520 Anniversary party
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1520 题意:职员之间有上下级关系,每个职员有自己的happy值,越高在派对上就越能炒热气氛.但是必须是 ...
- [Codeforces743D][luogu CF743D]Chloe and pleasant prizes[树状DP入门][毒瘤数据]
这个题的数据真的很毒瘤,身为一个交了8遍的蒟蒻的呐喊(嘤嘤嘤) 个人认为作为一个树状DP的入门题十分合适,同时建议做完这个题之后再去做一下这个题 选课 同时在这里挂一个选取节点型树形DP的状态转移方程 ...
- HDU 4714 Tree2cycle(树状DP)(2013 ACM/ICPC Asia Regional Online ―― Warmup)
Description A tree with N nodes and N-1 edges is given. To connect or disconnect one edge, we need 1 ...
- poj2486--Apple Tree(树状dp)
Apple Tree Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 7789 Accepted: 2606 Descri ...
随机推荐
- Android广告页循环播放
摘要:项目要求做一个广告页,实现几秒更换一次广告页,下方还有指示第几张广告页,同样也支持手动左滑或右滑. 1.准备好粘贴5个有关广告页的类. ①BaseViewPager==>自定义高度的Vie ...
- WPF异步回调时回调函数如何获取异步函数产生的变量
有这么一个问题,WPF在使用异步回调的时候,回调函数需要用到异步函数里产生的一个变量,例如异步函数里查询数据库得到了一个DataTable,如何传递给回调函数呢? [方案一]使用全局变量 很容易想到的 ...
- 新手用WPF山寨QQ管家7.6(三)
由于一直忙工作,没有更新完博客,更可恨的是...在清理资料的时候不小心删除了之前自己做的各种效果的DEMO....好在项目中用到了大部分,也算有所保留,以后可不敢随便删东西了....太可怕了! 在 新 ...
- django 连接MYSQL时,数据迁移时报:django.db.utils.InternalError: (1366, "Incorrect string value: '\\xE9\\x97\\xAE\\xE9\\xA2\\x98' for column 'name' at row 5")
django 连接MYSQL时,数据迁移时报:django.db.utils.InternalError: (1366, "Incorrect string value: '\\xE9\\x ...
- SEO搜索引擎优化基础
要如何提高自己网站的知名度,那必须了解一些SEO知识. 1.什么是搜索引擎 所谓的搜索引擎(Search Engines)是一些能够主动搜索信息(搜索网页上的单词和简短的特定的内容描述)并将其自动索 ...
- PostgreSQL 数组类型
PostgreSQL 支持表的字段使用定长或可变长度的一维或多维数组,数组的类型可以是任何数据库内建的类型.用户自定义的类型.枚举类型, 以及组合类型.但目前还不支持 domain 类型. 数组类型的 ...
- MongoDB01——安装MangoDB
一.MongoDB的下载 到MongoDB的官网——https://www.mongodb.com/download-center/community,选择要下载的版本,点击Download 二.安装 ...
- Android版本对应的API号
Android版本 API 级别 1.5 API 3 1.6 API 4 2.1 API 7 2.2 API 8 2.3.3 API 10 3.0 API 11 3.1 API 12 3.2 API ...
- SVD简化数据
一,引言 我们知道,在实际生活中,采集到的数据大部分信息都是无用的噪声和冗余信息,那么,我们如何才能剔除掉这些噪声和无用的信息,只保留包含绝大部分重要信息的数据特征呢? 除了上次降到的PCA方法,本次 ...
- “取出数据表中第10条到第20条记录”的sql语句+selecttop用法
1.首先,select top用法: 参考问题 select top n * from和select * from的区别 select * from table -- 取所有数据,返回无序集合 sel ...