树的直径,树的最长路dp思想
2012-07-30 20:34
204 查看
dp一直弱死了,树型dp很多基本的题都不会,最近在刷树型dp的题,把关于树的最长路的思想总结一下:
树的直径:树中距离最远的两点间的距离。
下面说几道题:
hdu 2196:对于树上(双向边)的每一个节点求出与其距离最远的点的距离。
这个主要用的思想是两次dfs:一次dfs将无向图转化为有跟树(所以一开是一定要是建双向边,不然很可能wa或者tle,记录过程中可以开数组记入父亲节点,也可以在dfs递推过程中以栈的形式记录)求出每个跟节点到其所有的叶子节点的最远距离f[i]和g[i]。再一次dfs求出能够由父亲节点转化得到的最大距离h[i],求h[i]的过程中就有可能用到f[i]和g[i]了,因为如果i节点在其父亲j节点的最远距离f[j]中,那么f[i]就只能由g[j]或者h[j]得到,不然就由f[j]或者h[j]得到,具体可能说的不是特别清。两个dfs综合起来的复杂度只有O(E)
poj 1985:求树的直径。个人觉得大概有3种方法。
第一种是如上题hdu2196的写法,求出每个点的最远距离最后取最大值,这样不会增加太多的复杂度,因为每次dfs也都知识O(E)的复杂度。
View Code
树的直径应该就是以上几种方法了吧,不过从poj1985的三种方法可以得出用搜索的方法求某个点的最远的点的距离了,就是先对任意一个点求距离其最远的顶点,最后可以得到一条树的直径的两个端点,以这两个端点开始去遍历整棵树,两个端点到每个点的距离较大值就会是这个点在树上能够走的最远距离。手画了几个sample,觉得如果树有多条直径,这个结论也应该是正确的。
树的直径:树中距离最远的两点间的距离。
下面说几道题:
hdu 2196:对于树上(双向边)的每一个节点求出与其距离最远的点的距离。
这个主要用的思想是两次dfs:一次dfs将无向图转化为有跟树(所以一开是一定要是建双向边,不然很可能wa或者tle,记录过程中可以开数组记入父亲节点,也可以在dfs递推过程中以栈的形式记录)求出每个跟节点到其所有的叶子节点的最远距离f[i]和g[i]。再一次dfs求出能够由父亲节点转化得到的最大距离h[i],求h[i]的过程中就有可能用到f[i]和g[i]了,因为如果i节点在其父亲j节点的最远距离f[j]中,那么f[i]就只能由g[j]或者h[j]得到,不然就由f[j]或者h[j]得到,具体可能说的不是特别清。两个dfs综合起来的复杂度只有O(E)
poj 1985:求树的直径。个人觉得大概有3种方法。
第一种是如上题hdu2196的写法,求出每个点的最远距离最后取最大值,这样不会增加太多的复杂度,因为每次dfs也都知识O(E)的复杂度。
View Code
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> using namespace std; const int maxn = 40005; int f[maxn], g[maxn], longest[maxn]; vector<int> son[maxn], w[maxn]; int dfs(int root, int pre){ int i, j; int est = 0, esti=-1, er=0; if(f[root]!=-1) return f[root]; if(son[root].empty()) return f[root] = 0; for(i=0;i<son[root].size();i++){ if(pre!=son[root][i]){ if(dfs(son[root][i],root)+w[root][i]>est){ est = f[son[root][i]]+w[root][i]; esti = i; } } } longest[root] = esti; for(i=0;i<son[root].size();i++){ if(pre!=son[root][i]){ if(f[son[root][i]]+w[root][i]>er&&i!=longest[root]){ er = f[son[root][i]]+w[root][i]; } } } g[root] = er; return f[root] = est; } void Init(int n){ int i; for(i=0;i<=n;i++){ f[i] = g[i] = longest[i] = -1; son[i].clear(); w[i].clear(); } } int main(){ int i, j, k, n, m, ans; int x1, x2, l; char opt; while(~scanf("%d%d",&n,&m)){ Init(n); for(i=0;i<m;i++){ scanf("%d%d%d",&x1,&x2,&l); scanf(" %c",&opt); son[x1].push_back(x2);w[x1].push_back(l); son[x2].push_back(x1);w[x2].push_back(l); } for(i=1;i<=n;i++){ if(f[i]==-1){ f[i] = dfs(i,-1); } } ans = 0; for(i=1;i<=n;i++){ ans = max(ans,f[i]+g[i]); } printf("%d\n",ans); } return 0; }
树的直径应该就是以上几种方法了吧,不过从poj1985的三种方法可以得出用搜索的方法求某个点的最远的点的距离了,就是先对任意一个点求距离其最远的顶点,最后可以得到一条树的直径的两个端点,以这两个端点开始去遍历整棵树,两个端点到每个点的距离较大值就会是这个点在树上能够走的最远距离。手画了几个sample,觉得如果树有多条直径,这个结论也应该是正确的。
相关文章推荐
- hdu 4123 Bob’s Race (树的直径相关+rmq+单调队列思想)
- 面向对象的编程思想在javascript中的运用(上)
- 浅析COM的思想及原理
- 编程思想——访问权限控制
- 36、Selenium Page Objects 思想
- 罗素的自由思想十诫
- 平台+插件软件设计思想基于COM原型实现的代码剖析
- mapreduce的基本思想
- 嵌入式Linux设备驱动开发思想进阶之驱动分层与驱动分离
- C++编程思想(卷二):设计模式:策略模式
- [WPF]入门理解Binding 数据驱动思想
- raft算法的核心思想
- 算法设计与分析之分治思想
- 程序员职业规划哲理思想
- 连载:面向对象葵花宝典:思想、技巧与实践(28) - 设计原则:内聚&耦合
- Manager 的思想
- 线性时间求第k小(分治思想)
- 领域建模的思想和方法
- ID3算法思想以及实现
- AOP,OOP两种编程思想联系