NOI03 逃学的小孩
来源:互联网 发布:第四十一知乎 编辑:程序博客网 时间:2024/06/11 23:00
问题描述
Chris家的电话铃响起了,里面传出了Chris的老师焦急的声音:“喂,是Chris的家长吗?你们的孩子又没来上课,不想参加考试了吗?”一听说要考试,Chris的父母就心急如焚,他们决定在尽量短的时间内找到Chris。他们告诉Chris的老师:“根据以往的经验,Chris现在必然躲在朋友Shermie或Yashiro家里偷玩《拳皇》游戏。现在,我们就从家出发去找Chris,一但找到,我们立刻给您打电话。”说完砰的一声把电话挂了。
Chris居住的城市由N个居住点和若干条连接居住点的双向街道组成,经过街道x需花费Tx分钟。可以保证,任两个居住点间有且仅有一条通路。Chris家在点C,Shermie和Yashiro分别住在点A和点B。Chris的老师和Chris的父母都有城市地图,但Chris的父母知道点A、B、C的具体位置而Chris的老师不知。
为了尽快找到Chris,Chris的父母会遵守以下两条规则:
l 如果A距离C比B距离C近,那么Chris的父母先去Shermie家寻找Chris,如果找不到,Chris的父母再去Yashiro家;反之亦然
l Chris的父母总沿着两点间唯一的通路行走。
这题应该说是树形dp中难度较大的题了。具体参考陈瑜希的论文。
主要思想是通过两遍dfs。第一遍任意选取一个节点作为根,然后求得每个节点子树中前3远的距离。
第二遍dfs是对每个节点求其到其父亲节点引出来的其他节点的最远距离,重新调整每个节点的前3远的距离。
并且第二遍dfs有两个步骤,前一个步骤保证搜索的方向是层次的,这样就能够保证子树更新距离时,父亲节点已更新完毕。
然后为什么父亲节点会引出一条路径。其实是因为每个分叉结点的前3远节点在不同的三颗子树(以该节点为根),也就是三颗子树交于该点,而过该点从其父亲方向过来的只可能有一条,如果大于1条,则说明三颗子树交于多个点组成的线段。
然后可能有人会有疑问?如果仅有一条树链,那么三点不是不交于1点,但是因为我们枚举了所有分叉点,对于这条链上的中间那个点作为分叉点,并且按照论文中转换,也就是添加边权为0的节点,那么就符合题意了,这个在代码里赋初值时是有体现的。但可能有人又有疑问了,其他点作为分叉点,最优解不就不交于1点了。
没错,但是此时根据代码,这些点只会求得以三颗子树交于该点的最优解,因此这些点得不到最优解。但是因为枚举了所有点作为分叉点,所以必然会在以那个中间点作为分叉点时,得到最优解。
所以这些完全不需要担心。。。但是如果你有这些顾虑,那说明你思考问题相当严密,值得鼓励。
代码:
#include<cstdio>#include<iostream>#include<cstring>#include<vector>#include<algorithm>#define ll long long#define Maxn 200010using namespace std;struct edge{ int u,v; ll val;}p[Maxn];struct maxx{ int ori; ll dis; bool operator<(const maxx &a)const{ return dis>a.dis; }}ma[Maxn][4];vector<edge> dv[Maxn];ll ans;int vis[Maxn];void dfs1(int u){ vis[u]=1; for(int i=0;i<dv[u].size();i++){ int v=u==dv[u][i].u?dv[u][i].v:dv[u][i].u; if(!vis[v]&&u!=v){ dfs1(v); ma[u][3].dis=ma[v][0].dis+dv[u][i].val; ma[u][3].ori=v; sort(ma[u],ma[u]+4); ans=max(ans,ma[u][0].dis+2*ma[u][1].dis+ma[u][2].dis); } }}void dfs2(int u){ vis[u]=1; for(int i=0;i<dv[u].size();i++){ int v=u==dv[u][i].u?dv[u][i].v:dv[u][i].u; if(vis[v]&&u!=v){ if(ma[v][0].ori!=u) ma[u][3].dis=ma[v][0].dis+dv[u][i].val; else ma[u][3].dis=ma[v][1].dis+dv[u][i].val; sort(ma[u],ma[u]+4); ans=max(ans,ma[u][0].dis+2*ma[u][1].dis+ma[u][2].dis); break; } } for(int i=0;i<dv[u].size();i++){ int v=u==dv[u][i].u?dv[u][i].v:dv[u][i].u; if(!vis[v]&&u!=v) dfs2(v); }}int main(){ int n,m,val; scanf("%d%d",&n,&m); ans=0; for(int i=1;i<=n;i++) dv[i].clear(); for(int i=0;i<m;i++){ scanf("%d%d%lld",&p[i].u,&p[i].v,&p[i].val); dv[p[i].u].push_back(p[i]); dv[p[i].v].push_back(p[i]); } memset(vis,0,sizeof vis); memset(ma,0,sizeof ma); dfs1(1); memset(vis,0,sizeof vis); dfs2(1); printf("%lld\n",ans);return 0;}
- NOI03 逃学的小孩
- NOI 2003 逃学的小孩
- bzoj 1509 逃学的小孩
- BZOJ1509: [NOI2003]逃学的小孩
- BZOJ1509: [NOI2003]逃学的小孩
- bzoj 1509 逃学的小孩
- 1509: [NOI2003]逃学的小孩
- bzoj1509[NOI2003] 逃学的小孩
- 1509: [NOI2003]逃学的小孩
- bzoj1509: [NOI2003]逃学的小孩
- bzoj1509: [NOI2003]逃学的小孩
- NOI 2003 逃学的小孩【树形DP】
- BZOJ 1509: [NOI2003]逃学的小孩
- [BZOJ 1509][NOI 2003]逃学的小孩(树的直径)
- 【bzoj1509】 NOI2003 逃学的小孩 树的直径
- 【树的直径】bzoj 1509 逃学的小孩
- [bzoj1509][NOI2003]逃学的小孩 树的直径
- NOI2003(bzoj1509)逃学的小孩(树型dp,好题)
- 判断某一点在直线的左右侧的算法
- MAVEN学习笔记
- Android - ProGuard简介
- http协议 文件上传 POST
- 静态网页如何实现动态交互?-JavaScript
- NOI03 逃学的小孩
- Android Proguard混淆详解
- ORACLE数据库操作简便函数用法
- 通过UIView得到其UIViewController
- linux下解压命令大全
- 能力提高篇
- JavaScript之this释疑
- 都不知道我的人品好不好
- 2014年10月学习归纳与总结