树的直径

定义

树的直径定义:一棵树的直径就是这棵树上存在的最长路径。

实现方法

1、两次 bfs(或者dfs)

方法:先从任意一点 P 出发,找离它最远的点 Q ,再从点 Q 出发,找离它最远的点 W ,W 到 Q 的距离就是是的直径。

证明如下:

①若 P 已经在直径上,根据树的直径的定义可知 Q 也在直径上且为直径的一个端点。

②若 P 不在直径上,我们用反证法,假设此时 WQ 不是直径,AB 是直径。

BFS代码实现:

#include<bits/stdc++.h>
#define MAX 100000
using namespace std;
int head[MAX];
int vis[MAX];//标记当前节点是否已经用过 
int dis[MAX];//记录最长距离 
int n,m,ans;
int sum;//记录最长路径的长度 
int aga;
struct node
{
    int u,v,w;
    int next;
}edge[MAX];
void add(int u,int v,int w)//向邻接表中加边 
{
    edge[ans].u=u;
    edge[ans].v=v;
    edge[ans].w=w;
    edge[ans].next=head[u];
    head[u]=ans++;
}
void getmap()
{
    int i,j;
    int a,b,c;
    ans=0;
    memset(head,-1,sizeof(head));
    while(m--)
    {
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);
        add(b,a,c);
    }
}
void bfs(int beg)
{
    queue<int>q;
    memset(dis,0,sizeof(dis));
    memset(vis,0,sizeof(vis));
    int i,j;
    while(!q.empty())
        q.pop();
    aga=beg;
    sum=0;
    vis[beg]=1;
    q.push(beg);
    int top;
    while(!q.empty())
    {
        top=q.front();
        q.pop();
        for(i=head[top];i!=-1;i=edge[i].next)
        {
            if(!vis[edge[i].v])
            {
                dis[edge[i].v]=dis[top]+edge[i].w;
                vis[edge[i].v]=1;
                q.push(edge[i].v);
                if(sum<dis[edge[i].v])
                {
                    sum=dis[edge[i].v];
                    aga=edge[i].v;
                } 
            }
        }
    }
}
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        getmap();
        bfs(1);//搜索最长路径的一个端点 
        bfs(aga);//搜索另一个端点 
        printf("%d\n",sum);
    }
    return 0;
}

DFS代码实现

#include<bits/stdc++.h>
using namespace std;
const int N=100005;
int n,m,t,p,ans;
int d[N],first[N],v[N],w[N],next[N];
void add(int x,int y,int z)
{
	t++;
	next[t]=first[x];
	first[x]=t;
	v[t]=y;
	w[t]=z;
}
void dfs(int x,int father)
{
	int i,j;
	if(ans<d[x])
	{
		ans=d[x];
		p=x;
	}
	for(i=first[x];i;i=next[i])
	{
		j=v[i];
		if(j==father)
		  continue;
		d[j]=d[x]+w[i];
		dfs(j,x);
	}
}
void find(int x)
{
	ans=0;
	d[x]=0;
	dfs(x,0);
}
int main()
{
	int x,y,z,i;
	scanf("%d%d",&n,&m);
	for(i=1;i<=m;++i)
	{
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z);
		add(y,x,z);
	}
	find(1);
	find(p);
	printf("%d",ans);
	return 0;
}

2.树形 DP

对于每个节点我们要记录两个值:

f1 [ i ] 表示以 i 为根的子树中,i 到叶子结点距离的最大值

f2 [ i ] 表示以 i 为根的子树中,i 到叶子结点距离的次大值

对于一个节点,它到叶子结点距离的最大值和次大致所经过的路径肯定是不一样的

ji的儿子,那么(下面的 w [ i ][ j ] 表示 ij 的路径长度):

f1 [ i ] < f1 [ j ] + w [ i ][ j ],f2 [ i ] = f1 [ i ],f1 [ i ] = f1 [ j ] + w [ i ][ j ]
否则,若 f2 [ i ] < f1 [ j ] + w [ i ][ j ],f2 [ i ] = f1 [ j ] + w [ i ][ j ]
理解:这样做就是,先看能否更新最大值,若能,它的次大值就是原先的最大值,再更新它的最大值;若不能,就看能不能更新次大值,若能,就更新,不能就不管它

这样的话,最后的答案 answer = max { f1 [ i ] + f2 [ i ] }

DP代码(这是从叶节点到根节点的DP):

#include<bits/stdc++.h>
using namespace std;
const int N=100005;
int n,m,t,ans;
int f1[N],f2[N];
int first[N],v[N],w[N],next[N];
void add(int x,int y,int z)
{
	t++;
	next[t]=first[x];
	first[x]=t;
	v[t]=y;
	w[t]=z;
}
void dp(int x,int father)
{
	int i,j;
	for(i=first[x];i;i=next[i])
	{
		j=v[i];
		if(j==father)
		  continue;
		dp(j,x);
		if(f1[x]<f1[j]+w[i])
		{
			f2[x]=f1[x];
			f1[x]=f1[j]+w[i];
		}
		else if(f2[x]<f1[j]+w[i])
		  f2[x]=f1[j]+w[i];
		ans=max(ans,f1[x]+f2[x]);
	}
}
int main()
{
	int x,y,z,i;
	scanf("%d%d",&n,&m);
	for(i=1;i<=m;++i)
	{
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z);
		add(y,x,z);
	}
	dp(1,0);
	printf("%d",ans);
	return 0;
}
posted @ 2022-04-16 07:16  PassName  阅读(125)  评论(0编辑  收藏  举报