• 树上前缀和学习应用笔记——树的直径


    By pjx,拿走请附上https://www.cnblogs.com/pjxpjx/p/12432463.html

    树的直径

    零、前言

    这是几个月前写了一半的东西好大一口锅,填个坑

    *如果没有学过树上前缀和,建议先阅读该文献

    一、定义

    一些无向边且权值均为正整数构成的树上(可以无根),距离最长的2个点的距离为树的直径。

    二、解法(边权为例)

    1、思路

    先任意从一点(a) 出发,找到离它最远的那个点(b)

    再从(b) 出发,找到离(b) 最远的点(c)

    (b)(c) 的距离即为树的直径。

    下面给出证明:

    2、证明

    不难得证,因为树上每两点间只可能有(1)条路径,长度都唯一,

    分情况讨论:

    1、如果(a) 点就为树的直径的起始点,则找到的(b) 点就是终点,再以(b) 为起点,又回到(a) 点;

    2、如果(a) 不为树直径的两个端点,则(b)(a) 最远。如果(b) 不为直径的端点,那么一定有一个点比(b) 点远,那个点就是直径的端点。(因为为正整数权值,(b) 连向直径的端点那一段路也会让权值变得更大

    综上,(b) 便为直径的一个端点,与(b) 点最远的点就一定是另一个端点了。

    得证。

    3、树上前缀和 + DFS大法

    如何找出最远的点呢?前缀和是个好东西。先跑一边从(a) 开始的前缀和,找到 (b),从(b) 跑前缀和,找到(c), 输出前缀和那个值就行了。

    三、code:

    #include <iostream>
    #include <cstdlib>
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #include <queue>
    using namespace std;
    const int N = 10005;
    int b[N], cnt, n , m, head[N], total[N], total2[N], b2[N];
    struct node{
    	int u, v, w, next;
    }ed[N];
    void add_edge(int u,int v,int w)//邻接表存图
    {
    	cnt++;
    	ed[cnt].u=u;
    	ed[cnt].v=v;
    	ed[cnt].w=w;
    	ed[cnt].next=head[u];
    	head[u]=cnt;
    }
    void dfs(int xx)//第一次前缀和
    {
    	//前面没有赋自己的值,不要弄混两个前缀和 
    	for(int i=head[xx];i!=0;i=ed[i].next)
    	{
    		int temp=ed[i].v;
    		if(!b[temp])
    		{
    			b[temp]=1;
    			total[temp]=ed[i].w+total[xx];
    			dfs(temp);
    		}
    	}
    }
    void dfs2(int xx)//这是第二次前缀和
    {
    	for(int i=head[xx];i!=0;i=ed[i].next)
    	{
    		int temp=ed[i].v;
    		if(!b2[temp])
    		{
    			b2[temp]=1;
    			total2[temp]=ed[i].w+total2[xx];
    			dfs2(temp);
    		}
    	}
    }
    int main()
    {
    	cin>>n;
    	for(int i=1;i<=n-1;i++)
    	{
    		int x,y,k;
    		cin>>x>>y>>k;
    		add_edge(x,y,k);
    		add_edge(y,x,k);
    	}
    	b[1]=1;
    	dfs(1);//第一遍,从a找到b
    	int root,maxn=-1;
    	for(int i=1;i<=n;i++)
    	{
    		if(total[i]>maxn)//找出最大值
    		{
    			maxn=total[i];
    			root=i;
    		}
    	}
    	b2[root]=1;
    	dfs2(root);//第二遍,从b再找到c
    	int root2,maxn2=-1;
    	for(int i=1;i<=n;i++)
    	{
    		if(total2[i]>maxn2)//找出最大值
    		{
    			maxn2=total2[i];
    			root2=i;
    		}
    	}
    	cout<<maxn2;
    	return 0;
    }
    /*
    Input:
    7 
    1 2 2
    1 3 1
    5 6 3
    4 6 4
    3 5 1
    5 7 3
    Output:
    11
    */
    

    样例数据如图所示:

    自己程序结果如图:

    四、模板练习

    poj1985

    我的题解

  • 相关阅读:
    redis的发布与订阅机制
    三次握手与四次挥手详解
    super的实例及实现原理
    【node.js】入门篇
    简单理解什么是数据库CDC?(以mysql为例)
    Java小工具类(一)json的K-V转换为Java类属性
    linux系统文件拷贝命令rsync
    linux系统配置常用命令top
    关于ganymed-ssh2版本262和build210的SCPClient类的区别
    阿里巴巴java-数据库开发手册(2020泰山版)
  • 原文地址:https://www.cnblogs.com/pjxpjx/p/12432463.html
Copyright © 2020-2023  润新知