图和树(上)图的遍历问题

树的直径问题

题目描述

给定一棵树,求树的直径,树的直径被定义为树中任意两点之间的距离的最大值,树的边权都为1

做法

首先明确树的直径一定是某两个叶子之间的距离
从树中任选一个点开始遍历这棵树,找到距离这个点最远的叶子,然后在从这个叶子开始遍历,找到里这个叶子最远的另一个叶子,他俩之间的距离就是树的直径。

两次遍历就可以求得树的直径

遍历可以使用DFS、BFS,找到距离起点最远的叶子节点就好。

题目描述

给定一棵树,输出n个数,第i个数表示第i个点到其他任意一点的最长距离。树的边权是1

树
这个例子中答案:3 2 3 4 4

首先回忆求树的直径的方法,假设树的最长路的两个叶子节点为v1,v2,从任意一点u出发走到最远点一定是(v1,v2)中的一点,然后再从v1或v2出发,走到最远点一定是v2或者v1,由此经过两此遍历就能找到最长路径。
某个节点x的最长路径要么是到v1的路径,要么是到v2的路径。
那么需要三次遍历,一次是从根出发,走到v1,从v1出发记录每个点距离v1点的距离。
记录下v2点,从v2点开始遍历,再记录下最长的距离。
三次遍历就额可以求解。

CSP-201403-4无线网路

问题描述
如果没有K的限制,那么就可以把m个位置都放上路由器,只要是两个路由器之间的距离不超过r,连个路由器就可以连一条边,那么就可以建立网络
求图中两个点之间的距离,途中所有变得权值为1
进行BFS,第一次遇到终点时求的距离便是两者之间的最短路。

现在加上k的限制。
我们将路由器分成两部分,一部分已经放好的路由器,一部分受k限制的路由器。
我们再进行BFS遍历的时候,可以记录下从源点,遍历到这个点的时候已经添加了几个受限路由器。
如果走到该点的时候,已经走了K个受限的路由器,紧接着之后就在遍历该点的邻接点时,就不能走受限路由器了。
走完bfs就是结果。

例题:

树的直径的应用

你现在是城市的主人
现在有 n 个村庄,已经修建了 n-1 条道路,使得各个村庄作为节点,路作为边,构成一棵树。
假设第 a 个村庄到第 b 个村庄有路相连,则从 a 走到 b 或者从 b 走到 a 需要走 1m 。

你需要输出 n 个数,第 i 个数代表从第 i 个村庄出发,距离他最远的村庄的距离
输入输出描述
思路: 找到树的直径,到任意村庄的距离,就是到某一直径的距离。
一次dfs,找到一个直径的端点,从该端点出发,进行dfs将每个节点到该端点的距离记录到数组中。同时找到另一个端点。从另一个端点出发,再次dfs,更新数组中的值。
结果输出数组

#include<bits/stdc++.h>
using namespace std;
#define MAXN  100015
//使用链式前向星进行存储
struct Edge{
    int u,v,w,nxt;
}Edges[2*MAXN];

int head[MAXN], tot,cnt_max = 0,far_left;//tot 是Edges的下标
bool vis[MAXN];
int dis[MAXN];
void init(int n)
{
    
    tot = 0;
    memset(head,-1,sizeof(head));
}

void addEdge(int u,int v,int w){
    Edges[tot].u = u;
    Edges[tot].v = v;
    Edges[tot].w = w;
    Edges[tot].nxt = head[u];
    head[u] = tot;
    tot++;
}

//链式前向星dfs遍历
void dfs(int u,int cnt){
    for(int i=head[u];i!=-1; i=Edges[i].nxt)
    {
        if(!vis[Edges[i].v])
         {
            vis[Edges[i].v] = true;
            dfs(Edges[i].v,cnt+1);
        }
    }
    if(cnt_max < cnt)
       {
           cnt_max = cnt;
           far_left = u;
       }
    if(cnt > dis[u])
    dis[u] = cnt;
}

int main()
{
    //freopen("6_a.in","r",stdin);
    //freopen("6_a.out","w",stdout);
    int n,a,b;
    cin>>n;
    init(n);
    memset(vis,0,sizeof(vis));
    memset(dis,0,sizeof(dis));
    for(int i=0;i < n-1;i++)
    {
        cin>>a>>b;
        //道路双向
        addEdge(a,b,1);
        addEdge(b,a,1);
    }
    vis[1] =1;
    dfs(1,0);


    int v1 = far_left;
    memset(vis,0,sizeof(vis));
    vis[v1]=1;
    dfs(v1,0);



    int v2 = far_left;
    memset(vis,0,sizeof(vis));
    vis[v2]=1;
    dfs(v2,0);

     for(int i=1;i <= n;i++)
        cout<<dis[i]<<" ";
    cout<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值