您的位置:首页 > 其它

树的直径,树的最长路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

#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,觉得如果树有多条直径,这个结论也应该是正确的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: