zoukankan      html  css  js  c++  java
  • 树上前缀和学习应用笔记——树的直径

    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

    我的题解

  • 相关阅读:
    PAT顶级 1024 Currency Exchange Centers (35分)(最小生成树)
    Codeforces 1282B2 K for the Price of One (Hard Version)
    1023 Have Fun with Numbers (20)
    1005 Spell It Right (20)
    1092 To Buy or Not to Buy (20)
    1118 Birds in Forest (25)
    1130 Infix Expression (25)
    1085 Perfect Sequence (25)
    1109 Group Photo (25)
    1073 Scientific Notation (20)
  • 原文地址:https://www.cnblogs.com/pjxpjx/p/12432463.html
Copyright © 2011-2022 走看看