树的直径 poj1985 poj 1849 hdu 2196

资料:http://www.cnblogs.com/celia01/archive/2012/07/30/2615842.html
http://www.cnblogs.com/wuyiqi/archive/2012/04/08/2437424.html
树的直径:树中最长路径的长度。
方法:假设最长路径为s-t ,对于任意一点u,找一条以u为起点的最长路径,这条路径的终点必为s、t 中的一点,从该点开始找最长路径,得到的路径就是树中的最长路径,也就是树的直径。
实现:对于任意一点为起点运用dfs,找到最长路,并记录下终点,在用这个终点作为起点,再做一次dfs,再找到的那一条最长路的长度就是树的直径。(当然这里求最长路的时候运用dfs或者是bfs都是可以的)。
对于这个方法的证明:
1)假设u是s-t路径中的一点,第一次终点必是s、t中的一个,如果不是,假设最远点是T那么dis(u,T) > dis(u,s) && dis(u,T) > dis(u,t) ,这与最长路径是s-t是矛盾的。 所以第一次终点必是s、t中的一个。那么这个方法是显然可以的。
2)若u不为s、t中的一点。
首先直到u如果走到了s-t路径上的话,那么之后的路径也在s-t上,终点必为s或t。
现在分类:
1、 如果u走到s-t路径上的某一点。接下来就会走到s或t上。按照这个方法也是可以求出最长路径的。
2、如果没有走到s-t路径上的一点,也就是u走的最远路径假设为u-T与s-t路径没有交点,那么dis(u,T) > dis(u,s) && dis(u,T) > dis(u,t),对于第二个式子同时加上dis(u,s)则 dis(T,u)+dis(u,s)=dis(u,T)>dis(u,s)+dis(u,t)=dis(u,t) 与之前s-t为最长的假设矛盾。所以这种情况不存在。
得证。
poj 1985 直接求树的直径

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define M 100009
#define INF 0x3f3f3f3f
typedef pair<int,int> pii;
vector<pii> G[M];
int n,m;
int maxlen,s;
void dfs(int st,int pre,int len)
{
    if(maxlen < len)
    {
        s = st;
        maxlen = len;
    }
    for(int i = 0;i < G[st].size();i++)
    {
        pii tmp = G[st][i];
        if(tmp.first == pre) continue;
        dfs(tmp.first,st,len + tmp.second);
    }
}
int main()
{
    //freopen("in.txt","r",stdin);
    while(scanf("%d %d",&n,&m) == 2)
    {
        for(int i = 0;i <= n;i++) G[i].clear();
        for(int i = 0;i < m;i++)
        {
            int a,b,c;
            char ch;
            scanf("%d %d %d %c",&a,&b,&c,&ch);
            G[a].push_back(make_pair(b,c));
            G[b].push_back(make_pair(a,c));
        }
        maxlen = -1;
        dfs(1,-1,0);
        maxlen = -1;
        dfs(s,-1,0);
        printf("%d\n",maxlen);
    }
    return 0;
}

poj 1849
题意: 让两个人遍历这个树,不需要返回起点,求花费经过的最短的路径。
思路:如果让一个人遍历这个树并且要返回起点的话,那么花费一定是整个树所有边权值和的两倍。让两个人遍历树,不用返回起点的话,最小花费就是用所以边权值和的两倍 - 树的直径

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
#define M 100009
#define INF 0x3f3f3f3f
struct node
{
    int v,w;
};
vector<node> G[M];
int n,s;
int ss,maxlen;
void dfs(int st,int pre,int len)
{
    for(int i = 0;i < G[st].size();i++)
    {
        node tmp = G[st][i];
        if(tmp.v != pre) dfs(tmp.v,st,len + tmp.w);
        else
        {
            if(maxlen < len)
            {
                maxlen = len;
                ss = st;
            }
        }
    }
}
int main()
{
    //freopen("in.txt","r",stdin);
    while(scanf("%d %d",&n,&s) == 2)
    {
        int sum = 0;
        for(int i = 0;i < n-1;i++)
        {
            int a,b,ww;
            scanf("%d %d %d",&a,&b,&ww);
            G[a].push_back(node{b,ww});
            G[b].push_back(node{a,ww});
            sum += 2*ww;
        }
        maxlen = -INF;
        dfs(s,-1,0);
        maxlen = -INF;
        dfs(ss,-1,0);
        printf("%d\n",sum - maxlen);
    }
    return 0;
}

hdu 2196
题意:求每一个点为起点的最长路径的长度。
同样运用树的直径的方法,运用两次dfs 都找最长路, 如果在这个过程中路径的长度大于这个点为起点的最长路径的长度,那么就更新,第一次dfs后就会到达直径的一个端点处。再来一次就可以更新除了这个端点之外其他点的最长路径,并且到达的终点是直径的另一个端点。但是这个后之前以那个直径的端点的最长路径还没有更新,再从第二次dfs 的终点为起点dfs一次,就可以更新之前那个直径的端点了。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define M 100009
#define INF 0x3f3f3f3f
typedef pair<int,int> pii;
vector<pii> G[M];
int n,maxlen,dis[M];
int s;
void dfs(int st,int pre,int len)
{
    if(maxlen < len)
    {
        maxlen = len;
        s = st;
    }
    if(dis[st] < len) dis[st] = len;
    for(int i = 0;i < G[st].size();i++)
    {
        pii tmp = G[st][i];
        if(tmp.first == pre) continue;
        dfs(tmp.first,st,len + tmp.second);
    }
}
int main()
{
    //freopen("in.txt","r",stdin);
    while(scanf("%d",&n) == 1)
    {
        for(int i = 0;i <= n;i++)
        {
            G[i].clear();
            dis[i] = -1;
        }
        for(int i = 0;i < n-1;i++)
        {
            int a,b;
            scanf("%d %d",&a,&b);
            G[i+2].push_back(make_pair(a,b));
            G[a].push_back(make_pair(i+2,b));
        }
        maxlen = -1; 
        dfs(1,-1,0);
        maxlen = -1; //每次都要记得还原maxlen避免失效
        dfs(s,-1,0);
        maxlen = -1;
        dfs(s,-1,0); //再一次dfs 以保证更新之前的终点即树的直径的一个端点
        for(int i = 1;i <= n;i++) printf("%d\n",dis[i]);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值