树的基础定义

【无根树】一棵没有固定根结点的树(树→图:无向图)
(补充一)无根树可以任意指定一个节点作为根节点,将根节点“提起”,其它节点自然“垂下”
【无根树】在无根树的基础上,指定一个结点称为根 (树→图:有向图)
(补充二)有根树在很多时候仍以无向图表示,只是规定了结点之间的上下级关系

Part One 适用于无根树&有根树

  • 森林(Forest):每个连通分量(连通块)都是树的图。按照定义,一棵树也是森林
  • 生成树(Spanning Tree):一个连通无向图的生成子图,同时要求是树。即在图的边集中选择N-1条,将所有顶点连通
  • 结点的深度(Depth)到根结点的路径上的边数
  • 结点的度数(Degree):结点拥有子结点数量
  • 树的高度(Height):所有结点的深度最大值
  • 无根树叶结点(Leaf Node):度数不超过1的结点(N==1时,只有一个结点,度数为0)
  • 有根树叶结点(Leaf Node)没有子结点的结点。

Part Two 只适用于有根树

  • 父亲结点**(Parent Node)从该结点到根路径上的第二个**结点(自己是第一个)根结点没有父结点
  • 祖先结点**(Ancestor):一个结点到根结点的路径上**,除了它本身外的结点;根结点的祖先集合为空
  • 结点**(Child Node):如果 u 是 v 的父亲,那么 v 是 u 的子结点(二叉树区分子结点顺序**)
  • 兄弟结点**(Sibling)同一个父亲的多个子结点**互为兄弟
  • 后代(Descendant)子结点和子结点的后代**;如果 u 是 v 的祖先,那么 v 是 u 的后代(有些地方称子孙结点
  • 子树**(Subtree)**:删掉与父亲相连的边后,该结点所在的子图

特殊的树

  • 链(Chain/Path Graph):满足与任一结点相连的边不超过2条的树
  • 菊花/星星(Star):满足存在 u 使得所有除 u 以外结点均与 u 相连的树
  • 二叉树(Binary Tree):每个结点最多只有两个儿子子结点,且分子结点和子结点)
  • 有根二叉树 (Rooted Binary Tree)二叉树中的有根树;大多数情况下, 二叉树一词均指有根二叉树
  • 完整二叉树 (Full/Proper binary tree)每个结点子结点数量均为 0 或者 2叶子结点or左右子树均非空)的二叉树
  • 完全二叉树 (Complete Binary Tree)仅叶子结点度数为1其他度数都达到最大叶子结点从左到右依次排布
  • 完美二叉树 (Perfect Binary Tree)所有叶子结点深度相同的二叉树(二叉树:多指完美二叉树)

树的存储

Part One 链式前向星

#define fzhead EDGE(int _from,int _to,int _w,int _next)
#define fzbody from(_from),to(_to),w(_w),next(_next)
using namespace std;
const int maxn=1e5+10;
int head[maxn],edgecount;
struct EDGE
{int from,to,w,next;
/*Edge(){}是个用来给变量初始化0的函数fzhead:fzbody{}为结构体赋初值
*/EDGE(){}//后面别加分号 fzhead:fzbody{}//后面别加分号
}edge[maxn];
void init()
{memset(head,-1,sizeof(head));edgecount=-1;
}
/*i:边的编号;从u到v;w:权值;next:下一条边的编号head[i]:由i出发的第一条边的编号仅插入一条从u1开始的边,则next是-1,head[u1]为该边的编号再读入u1开始的边,则其为第一条边,next是上一条边的编号
*/
void addEdge(int from,int to,int w)//链序和读入顺序相反
{edge[++edgecount]=EDGE(from,to,w,head[from]);head[from]=edgecount;
}

Part Two Vector邻接表

#define fzhead EDGE(int _to,int _w)
#define fzbody to(_to),w(_w)
using namespace std;
const int maxn=1e5+10;
int n;
struct EDGE
{int to,w;
/*Edge(){}是个用来给变量初始化0的函数fzhead:fzbody{}为结构体赋初值
*/EDGE(){}//后面别加分号 fzhead:fzbody{}//后面别加分号
};
vector<EDGE> edge[maxn];
void init()
{for(int i=1;i<=n;++i) edge[i].clear();
}
/*i:边的编号;从u到v;w:权值;next:下一条边的编号head[i]:由i出发的第一条边的编号仅插入一条从u1开始的边,则next是-1,head[u1]为该边的编号再读入u1开始的边,则其为第一条边,next是上一条边的编号
*/
void addEdge(int from,int to,int w)//链序和读入顺序相反
{edge[from].push_back(EDGE(to,w));
}

树的遍历

前言

  • 按照结点被访问的顺序,进行标号,可以形成DFS序BFS序
  • 【DFS序的性质】
  • 记录某结点第一次被访问到的序号第二次被访问到的序号(也就是递归完退回来时候的序号)
    两个序号之差就是子树的大小,同时两个序号之间连续的一段都在子树内
    也就是说子树的关系压缩到一维了,可以用一维的数据结构来维护了,比如线段树
  • 对于树上任何一个结点,它祖先的DFS序一定小于自己
  • 【BFS序的性质】
  • 深度越深的结点标号越大,按照标号从大到小遍历,就相当于从子树向根遍历了,不用递归

二叉树的深度优先搜索

  • 先序遍历
    先访问根节点,然后依次访问左儿子右儿子
  • 中序遍历
    先访问左儿子,然后依次访问根节点右儿子
  • 后序遍历
    先访问左儿子,然后依次访问右儿子根节点
  • 已知中序遍历另外一个,可以还原整颗树
  • ps:三序遍历的顺序可按照根结点被遍历的位置来记忆

Part One DFS深度优先搜索

**以链式前向星存储为例**
void DFS(int root)//传入根结点序号
{num[root]=1;vis[root]=true;for(int i=head[root];i!=-1;i=edge[i].next){if(vis[edge[i].to]==true) continue;//如果子结点已被访问则跳过(防无向图)DFS(edge[i].to);}
}

Part Two BFS广度优先搜索

**以链式前向星存储为例**
void BFS(int u)//传入根结点序号
{Max=0;memset(vis,0,sizeof(vis));queue<int> Q;Q.push(u);vis[u]=true;while(!Q.empty()){int root=Q.front();Q.pop();for(int i=head[root];i!=-1;i=edge[i].next){if(vis[edge[i].to]==true) continue;//如果子结点已被访问则跳过(防无向图)Q.push(edge[i].to);vis[edge[i].to]=true;}}
}

树的重心

定义

  • 对于树上的每一个点,计算其所有子树最大的子树结点数,这个值最小的点就是这棵树的重心
  • 此定义中的 “子树”指无根树的子树,即包括“向上”的那棵子树,并且不包括整棵树自身

性质

  • 树的重心为根时,所有子树大小不超过整棵树大小的一半
  • 树中所有点到某个点距离和中,到重心距离和最小的;如果有两个重心,那么到它们的距离和一样
  • 两棵树通过一条边相连得到一棵新的树,那么新的树的重心连接原来两棵树重心路径上
  • 一棵树上添加删除一个叶子,那么它的重心最多移动一条边的距离

求法(代码)

int get_barycenter(int root,int dady)
{size[root]=1;//记录一棵子树的结点数(一棵子树的大小),root作根结点初始大小为1 Max[root]=0;for(int i=head[root];i!=-1;i=edge[i].next){if (edge[i].to!=dady){get_barycenter(edge[i].to,root);//递归,从叶子结点大小为 1 开始回推赋值 size[root]+=size[edge[i].to];//结点数累加,对root根结点而言的 Max[root]=max(Max[root],size[edge[i].to]);//对root子树而言的 //对于树上的每一个root,计算其所有子树中最大的子树结点数 }}Max[root]=max(Max[root],n-size[root]);/*n为总结点数,定义中的**子树**指无根树的子树size[edge[i].to]是向下的;n-size[root]是向上的减去自身这整棵树的结点数,剩下的就是向上的子树的结点数啦 用向下的最大值,与向上的比较得到最终的最大值 */if (center==0||Max[root]<Max[center]) center=root;//center初始化为0,还未被更新过值或得到更小的最大子树结点数,就用当前root更新重心center return center;//center为重心编号
}

例题 POJ-2378-Tree Cutting

Description
After Farmer John realized that Bessie had installed a “tree-shaped” network among his N (1 <= N <= 10,000) barns at an incredible cost, he sued Bessie to mitigate his losses.
Bessie, feeling vindictive, decided to sabotage Farmer John’s network by cutting power to one of the barns (thereby disrupting all the connections involving that barn). When Bessie does this, it breaks the network into smaller pieces, each of which retains full connectivity within itself. In order to be as disruptive as possible, Bessie wants to make sure that each of these pieces connects together no more than half the barns on FJ.
Please help Bessie determine all of the barns that would be suitable to disconnect.

Input

  • Line 1: A single integer, N. The barns are numbered 1…N.
  • Lines 2…N: Each line contains two integers X and Y and represents a connection between barns X and Y.

Output

  • Lines 1…?: Each line contains a single integer, the number (from 1…N) of a barn whose removal splits the network into pieces each having at most half the original number of barns. Output the barns in increasing numerical order. If there are no suitable barns, the output should be a single line containing the word “NONE”.

Sample Input
10
1 2
2 3
3 4
4 5
6 7
7 8
8 9
9 10
3 8

Sample Output
3
8

Hint
INPUT DETAILS:
The set of connections in the input describes a “tree”: it connects all the barns together and contains no cycles.
OUTPUT DETAILS:
If barn 3 or barn 8 is removed, then the remaining network will have one piece consisting of 5 barns and two pieces containing 2 barns. If any other barn is removed then at least one of the remaining pieces has size at least 6 (which is more than half of the original number of barns, 5).

AC代码

#include<stdio.h>
#include<string.h>
#include<vector>
#include<queue>
#include<algorithm>
#define fzhead EDGE(int _from,int _to,int _next)
#define fzbody from(_from),to(_to),next(_next)
using namespace std;
const int maxn=1e5+10;
int head[maxn],edgecount,n,num[maxn];
bool vis[maxn];
vector<int> ans;
void init()
{memset(vis,false,sizeof(vis));memset(head,-1,sizeof(head));memset(num,0,sizeof(num));edgecount=-1;ans.clear();
}
/*i:边的编号;从u到v;w:权值;next:下一条边的编号head[i]:由i出发的第一条边的编号仅插入一条从u1开始的边,则next是-1,head[u1]为该边的编号再读入u1开始的边,则其为第一条边,next是上一条边的编号
*/
struct EDGE
{int from,to,next;
/*Edge(){}是个用来给变量初始化0的函数fzhead:fzbody{}为结构体赋初值
*/EDGE(){}//后面别加分号 fzhead:fzbody{}//后面别加分号
}edge[maxn];
/*i:边的编号;从from到to;w:权值,此处已删除;next:下一条边的编号head[i]:由i出发的第一条边的编号仅插入一条从u1开始的边,则next是-1,head[u1]为该边的编号再读入u1开始的边,则其为第一条边,next是上一条边的编号
*/
void addEdge(int from,int to)//链序和读入顺序相反
{edge[++edgecount]=EDGE(from,to,head[from]);head[from]=edgecount;
}
void DFS(int root)
{num[root]=1;vis[root]=true;int flag=1;for(int i=head[root];i!=-1;i=edge[i].next){if(vis[edge[i].to]==true) continue;DFS(edge[i].to);num[root]+=num[edge[i].to];if(num[edge[i].to]>n/2) flag=0;//root删除后,它的每个子结点牵着一棵树}if(n-num[root]<=n/2&&flag) ans.push_back(root);/*删除root结点后,其牵着的整个子树拿走,剩余结点满足条件且它的子树们,各自都满足条件 */
}
int main()
{while(~scanf("%d",&n)){init();int from,to;for(int i=1;i<n;++i){scanf("%d %d",&from,&to);addEdge(from,to);addEdge(to,from);}DFS(1);if(ans.empty()) scanf("NONE\n");else{sort(ans.begin(),ans.end());for(int i=0;i<ans.size();++i) printf("%d\n",ans[i]);}}return 0;
}

树的直径

定义

  • 对于树上任意的两点,他们之间的距离都是固定的(也就是路径上边权和)
  • 对于距离最大两点连成的,我们称为树的直径

性质

  • 对于树上任意一点x与x之间距离最远的点一定是直径两个端点之一
  • 对于两棵树,如果一棵直径是(u,v),另一棵是**(x,y)**
    一条边两棵树相连,则直径两端点必然是 (x,y,u,v) 之中两点

求法

  • 【First】从任意一个点出发找到离它最远的点x
  • 【Second】从x出发找到离x最远的点y,则x和y就是直径的两个端点

代码(以BFS为例)

int BFS(int u)//传入根结点
{Max=0;memset(dis,0,sizeof(dis));//距离数组memset(vis,0,sizeof(vis));//访问标记queue<int> Q;Q.push(u);vis[u]=true;int to=u;while(!Q.empty()){int root=Q.front();Q.pop();used[root]=true;for(int i=head[root];i!=-1;i=edge[i].next){if(!vis[edge[i].to])//如果子结点已被访问则跳过(防无向图){dis[edge[i].to]=dis[root]+edge[i].w;//从上往下累加总路程 if(dis[edge[i].to]>Max)//某一次比Max大的时候 {Max=dis[edge[i].to];to=edge[i].to;//用to标记Max最大时的终点 }Q.push(edge[i].to);//从Max最大的终点继续BFS,若无更大的Max则队列为空return to; vis[edge[i].to]=true;}}}return to;//返回的是从传入的根节点开始,最长路径的终点
}
**然后在int main()主函数中调用两次BFS函数**
int start=BFS(1);//第一次随便指定根结点
BFS(start);//第二次从返回的to(赋值给了start)开始(start为根结点,也是直径的一端)

例题 HDU-4514-湫湫系列故事——设计风景线

Description
  随着杭州西湖的知名度的进一步提升,园林规划专家湫湫希望设计出一条新的经典观光线路,根据老板马小腾的指示,新的风景线最好能建成环形,如果没有条件建成环形,那就建的越长越好。
  现在已经勘探确定了n个位置可以用来建设,在它们之间也勘探确定了m条可以设计的路线以及他们的长度。请问是否能够建成环形的风景线?如果不能,风景线最长能够达到多少?
  其中,可以兴建的路线均是双向的,他们之间的长度均大于0。

Input
  测试数据有多组,每组测试数据的第一行有两个数字n, m,其含义参见题目描述;
  接下去m行,每行3个数字u v w,分别代表这条线路的起点,终点和长度。
  [Technical Specification]
  1. n<=100000
  2. m <= 1000000
  3. 1<= u, v <= n
  4. w <= 1000

Output
  对于每组测试数据,如果能够建成环形(并不需要连接上去全部的风景点),那么输出YES,否则输出最长的长度,每组数据输出一行。

Sample Input
3 3
1 2 1
2 3 1
3 1 1

Sample Output
YES

AC代码

#include<stdio.h>
#include<string.h>
#include<vector>
#include<queue>
#include<algorithm>
#define fzhead EDGE(int _from,int _to,int _w,int _next)
#define fzbody from(_from),to(_to),w(_w),next(_next)
using namespace std;
const int maxn = 1e5+10;
int father[maxn],head[maxn],dis[maxn],edgecount,Max,n,m;
bool vis[maxn],used[maxn],tag;
void init()
{for(int i=1;i<=n;++i) vis[i]=false,father[i]=i;memset(head,-1,sizeof(head));memset(used,0,sizeof(used));memset(dis,0,sizeof(dis));edgecount=-1;
}
/*i:边的编号;从u到v;w:权值;next:下一条边的编号head[i]:由i出发的第一条边的编号仅插入一条从u1开始的边,则next是-1,head[u1]为该边的编号再读入u1开始的边,则其为第一条边,next是上一条边的编号
*/
struct EDGE
{int from,to,w,next;
/*Edge(){}是个用来给变量初始化0的函数fzhead:fzbody{}为结构体赋初值
*/EDGE(){}//后面别加分号 fzhead:fzbody{}//后面别加分号
}edge[maxn*20];
/*i:边的编号;从from到to;w:权值,此处已删除;next:下一条边的编号head[i]:由i出发的第一条边的编号仅插入一条从u1开始的边,则next是-1,head[u1]为该边的编号再读入u1开始的边,则其为第一条边,next是上一条边的编号
*/
void addEdge(int from,int to,int w)//链序和读入顺序相反
{edge[++edgecount]=EDGE(from,to,w,head[from]);head[from]=edgecount;
}
int find(int x){return x==father[x]?x:father[x]=find(father[x]);}
bool baba(int x,int y)
{int fx=find(x);int fy=find(y);if(fx==fy) return true;else{father[fx]=fy;return false;}
}
int BFS(int u)
{Max=0;memset(dis,0,sizeof(dis));memset(vis,0,sizeof(vis));queue<int> Q;Q.push(u);vis[u]=true;int to=u;while(!Q.empty()){int root=Q.front();Q.pop();used[root]=true;for(int i=head[root];i!=-1;i=edge[i].next){if(!vis[edge[i].to])//如果子结点已被访问则跳过(防无向图){dis[edge[i].to]=dis[root]+edge[i].w;//从上往下累加总路程 if(dis[edge[i].to]>Max)//某一次比Max大的时候 {Max=dis[edge[i].to];to=edge[i].to;//用to标记Max最大时的终点 }Q.push(edge[i].to);//从Max最大的终点处继续BFS,若无更大的Max了则队列为空 vis[edge[i].to]=true;}}}return to;//返回的是从传入的根节点开始,最长风景线的终点
}
int main()
{while(~scanf("%d %d",&n,&m)){init();tag=false;for(int i=1;i<=m;i++){int u,v,w;scanf("%d %d %d",&u,&v,&w);if(tag) continue;if(baba(u,v)) tag=true;//输出的同时处理是否成环,若已成环,无需再加边,后续直接输出YES addEdge(u,v,w);addEdge(v,u,w);}if(tag)//已成环 {printf("YES\n");continue;//跳过后续步骤,直接下一次输入 }int ans=-1;//用于比较得到最长风景线 for(int i=1;i<=n;i++){if(!used[i]){int start=BFS(i);/*从未被标记参与过最长风景线诞生的某点开始BFS找到最长风景线的其中一端然后再继续BFS找到另一端得到这一遍最后的Max 所有没有标记参与最长风景线诞生的点都遍历一次,找最大的Max 注意此题可能不止一棵树,一个未标记,就可能有一棵新的树 ***所以此时是在找不同树之间的最大直径!!!****/ BFS(start);ans=max(ans,Max);}}printf("%d\n",ans);}return 0;
}

树形DP

利用树形知识遍历求值等,实现状态转移

例题 HDU-1520-Anniversary party

Description
There is going to be a party to celebrate the 80-th Anniversary of the Ural State University. The University has a hierarchical structure of employees. It means that the supervisor relation forms a tree rooted at the rector V. E. Tretyakov. In order to make the party funny for every one, the rector does not want both an employee and his or her immediate supervisor to be present. The personnel office has evaluated conviviality of each employee, so everyone has some number (rating) attached to him or her. Your task is to make a list of guests with the maximal possible sum of guests’ conviviality ratings.

Input
Employees are numbered from 1 to N. A first line of input contains a number N. 1 <= N <= 6 000. Each of the subsequent N lines contains the conviviality rating of the corresponding employee. Conviviality rating is an integer number in a range from -128 to 127. After that go T lines that describe a supervisor relation tree. Each line of the tree specification has the form:
L K
It means that the K-th employee is an immediate supervisor of the L-th employee. Input is ended with the line
0 0
Output
Output should contain the maximal sum of guests’ ratings.

Sample Input
7
1
1
1
1
1
1
1
1 3
2 3
6 4
7 4
4 5
3 5
0 0

Sample Output
5

AC代码

#include<stdio.h>
#include<string.h>
#include<vector>
#include<queue>
#include<algorithm>
#define fzhead EDGE(int _from,int _to,int _next)
#define fzbody from(_from),to(_to),next(_next)
using namespace std;
const int maxn=2e5+10;
int head[maxn],edgecount,dp[maxn][2],n,ans;
bool vis[maxn];//DFS中标记是否已被访问
void init()
{memset(head,-1,sizeof(head));memset(vis,false,sizeof(vis));edgecount=-1;
}
/*i:边的编号;从u到v;w:权值;next:下一条边的编号head[i]:由i出发的第一条边的编号仅插入一条从u1开始的边,则next是-1,head[u1]为该边的编号再读入u1开始的边,则其为第一条边,next是上一条边的编号
*/
struct EDGE
{int from,to,next;
/*Edge(){}是个用来给变量初始化0的函数fzhead:fzbody{}为结构体赋初值
*/EDGE(){}//后面别加分号 fzhead:fzbody{}//后面别加分号
}edge[maxn];
/*i:边的编号;从from到to;w:权值,此处已删除;next:下一条边的编号head[i]:由i出发的第一条边的编号仅插入一条从u1开始的边,则next是-1,head[u1]为该边的编号再读入u1开始的边,则其为第一条边,next是上一条边的编号
*/
void addEdge(int from,int to)//链序和读入顺序相反
{edge[++edgecount]=EDGE(from,to,head[from]);head[from]=edgecount;
}
void DFS(int root)
{vis[root]=true;for(int i=head[root];i!=-1;i=edge[i].next){if(vis[edge[i].to]==true) continue;//如果子结点已被访问则跳过(防无向图) DFS(edge[i].to);//未被访问则递归到叶子结点 dp[root][0]+=max(dp[edge[i].to][0],dp[edge[i].to][1]);/*DP思路(状态转移方程思路):此根结点若不选,则它的子结点可选可不选此根结点若选,则它的子结点不可选递归出口是叶子节点,然后逆着返回 */ dp[root][1]+=max(0,dp[edge[i].to][0]);}
}
int main()
{while(~scanf("%d",&n)){init();for(int i=1;i<=n;++i){scanf("%d",&dp[i][1]);dp[i][0]=0;}int to,from;while(~scanf("%d %d",&to,&from)&&to+from){addEdge(to,from);addEdge(from,to);}DFS(1);//传入根结点 printf("%d\n",max(dp[1][0],dp[1][1]));//输出根结点选或不选的最大值 }return 0;
}

例题 HDU-2196-Computer

Description
A school bought the first computer some time ago(so this computer’s id is 1). During the recent years the school bought N-1 new computers. Each new computer was connected to one of settled earlier. Managers of school are anxious about slow functioning of the net and want to know the maximum distance Si for which i-th computer needs to send signal (i.e. length of cable to the most distant computer). You need to provide this information.

Hint: the example input is corresponding to this graph. And from the graph, you can see that the computer 4 is farthest one from 1, so S1 = 3. Computer 4 and 5 are the farthest ones from 2, so S2 = 2. Computer 5 is the farthest one from 3, so S3 = 3. we also get S4 = 4, S5 = 4.

Input
Input file contains multiple test cases.In each case there is natural number N (N<=10000) in the first line, followed by (N-1) lines with descriptions of computers. i-th line contains two natural numbers - number of computer, to which i-th computer is connected and length of cable used for connection. Total length of cable does not exceed 10^9. Numbers in lines of input are separated by a space.

Output
For each case output N lines. i-th line must contain number Si for i-th computer (1<=i<=N).

Sample Input
5
1 1
2 1
3 1
1 1

Sample Output
3
2
3
4
4

AC代码

**它涉及无根树非假定的根结点到其他节点的最远距离**
**考虑向上和向下的两种子树,并非树的直径类型题**
#include<stdio.h>
#include<string.h>
#include<vector>
#include<queue>
#include<algorithm>
#define fzhead EDGE(int _from,int _to,int _w,int _next)
#define fzbody from(_from),to(_to),w(_w),next(_next)
using namespace std;
const int maxn=1e5+10;
int head[maxn],edgecount,dp[maxn][3],n,test;
/*dp[i][0]为从下往上的最远距离dp[i][1]为从下往上的次远距离dp[i][2]为从经过root的最远距离
*/
bool vis[maxn];
void init()
{memset(vis,false,sizeof(vis));memset(head,-1,sizeof(head));memset(dp,0,sizeof(dp));edgecount=-1;
}
/*i:边的编号;从u到v;w:权值;next:下一条边的编号head[i]:由i出发的第一条边的编号仅插入一条从u1开始的边,则next是-1,head[u1]为该边的编号再读入u1开始的边,则其为第一条边,next是上一条边的编号
*/
struct EDGE
{int from,to,w,next;
/*Edge(){}是个用来给变量初始化0的函数fzhead:fzbody{}为结构体赋初值
*/EDGE(){}//后面别加分号 fzhead:fzbody{}//后面别加分号
}edge[maxn];
/*i:边的编号;从from到to;w:权值,此处已删除;next:下一条边的编号head[i]:由i出发的第一条边的编号仅插入一条从u1开始的边,则next是-1,head[u1]为该边的编号再读入u1开始的边,则其为第一条边,next是上一条边的编号
*/
void addEdge(int from,int to,int w)//链序和读入顺序相反
{edge[++edgecount]=EDGE(from,to,w,head[from]);head[from]=edgecount;
}
void DFS1(int root)//深搜从下往上的最远和次远距离
{int first=0,second=0;//first最远,second次远 //不可定义为全局变量,不然递归内部更改的同时外部也在更改,应当各自独立记录 vis[root]=true;for(int i=head[root];i!=-1;i=edge[i].next){if(vis[edge[i].to]==true) continue;//如果子结点已被访问则跳过(防无向图)DFS1(edge[i].to);test=dp[edge[i].to][0]+edge[i].w;//这一轮用于比较的test是已经从下往上形成的最远距离+此距离 if(test>=first)//更新最远(原最远变次远) {second=first;first=test;}else if(test>second) second=test;//更新次远 }dp[root][0]=first;//root结点从下往上的最远距离 dp[root][1]=second;//root结点从下往上的次远距离
}
void DFS2(int root)//求子结点们经过父结点的最远距离
{vis[root]=true;for(int i=head[root];i!=-1;i=edge[i].next){if(vis[edge[i].to]==true) continue;//如果子结点已被访问则跳过(防无向图)if(dp[edge[i].to][0]+edge[i].w==dp[root][0]) dp[edge[i].to][2]=max(dp[root][2],dp[root][1])+edge[i].w;else dp[edge[i].to][2]=max(dp[root][2],dp[root][0])+edge[i].w;/*遍历到父结点(root),求其子结点们经过父结点的最远距离1.根结点经过其父结点过来的的最远距离2.子结点从下往上的最远距离+此距离 判断是否等于 现根结点(root)从下往上的最远距离3.在 2 中,相等的话 返回根结点(root)从下往上的次远距离4.在 2 中,不相等的话 返回根结点(root)从下往上的最远距离+此距离5.求出前两者的最大值,加上这次的权值即可*//*因为要比较从上往下和从下往上哪个更远,所以两者的值得不同然后再用父结点向上或向下的最大值,加上现在的值,得到新值 如果if条件满足,依旧用dp[root][0]进行比较的话相当于root结点从下往上的最大值经过了现在要求的这个子结点 等于1-2-3这条链,想求2的最远路,走的却是3-2-1-2,有重合段了所以对于父结点从下往上的最远距离,必须保证不经过现在的这个子结点如果最大值经过,那么就用次大值进行比较再加上从父结点走向子结点的路程,得到子结点的新值 */DFS2(edge[i].to);//最后递归,因为计算表达式用到父结点,得顺着传值}
}
int main()
{while(~scanf("%d",&n)){init();int from,value;for(int i=2;i<=n;++i){scanf("%d %d",&from,&value);addEdge(from,i,value);}DFS1(1);//传入根结点序号 dp[1][2]=0;//初始化根结点经过其父结点过来的最远距离为0 memset(vis,false,sizeof(vis));//初始化均未被访问过 DFS2(1);//传入根结点序号 for(int i=1;i<=n;++i) printf("%d\n",max(dp[i][0],dp[i][2]));//一个结点的最远距离要么是往下走,要么是往上走经过根结点的}return 0;
}

#2020寒假集训#树形基础入门(Tree)代码笔记相关推荐

  1. AI Studio 飞桨 零基础入门深度学习笔记6.3-手写数字识别之数据处理

    AI Studio 飞桨 零基础入门深度学习笔记6.3-手写数字识别之数据处理) 概述 前提条件 读入数据并划分数据集 扩展阅读:为什么学术界的模型总在不断精进呢? 训练样本乱序.生成批次数据 校验数 ...

  2. AI Studio 飞桨 零基础入门深度学习笔记1-深度学习的定义

    AI Studio 飞桨 零基础入门深度学习-笔记 人工智能.机器学习.深度学习的关系 机器学习 机器学习的实现 机器学习的方法论 案例:牛顿第二定律 确定模型参数 模型结构介绍 深度学习 神经网络的 ...

  3. AI Studio 飞桨 零基础入门深度学习笔记4-飞桨开源深度学习平台介绍

    AI Studio 飞桨 零基础入门深度学习笔记4-飞桨开源深度学习平台介绍 深度学习框架 深度学习框架优势 深度学习框架设计思路 飞桨开源深度学习平台 飞桨开源深度学习平台全景 框架和全流程工具 模 ...

  4. AI Studio 飞桨 零基础入门深度学习笔记2-基于Python编写完成房价预测任务的神经网络模型

    AI Studio 飞桨 零基础入门深度学习笔记2-基于Python编写完成房价预测任务的神经网络模型 波士顿房价预测任务 线性回归模型 线性回归模型的神经网络结构 构建波士顿房价预测任务的神经网络模 ...

  5. 2020最新版python基础入门学习视频教程

    文末有本资源领取方式. Python作为编程语言中的黑马,2020年继续蝉联最受欢迎的编程语言.Python的就业持续火爆,在招聘岗位和薪资上都在持续上涨.如果你还是大学生,学习Python将使你在求 ...

  6. 零基础入门学习python笔记-day1:程序开发谋定而后动

    流程图 更复杂的开发需要用到思维导图哦~ 思维导图侧重于设计,流程图更侧重于实现. 学习资料:小甲鱼零基础入门学习python https://www.bilibili.com/video/BV1c4 ...

  7. 重视网络安全,红客联盟最新开源分享“黑客零基础入门学习路线+笔记”堪称黑客入门天花板教程

    起源 黑客这一词在莎士比亚的那个年代就存在了,最早的计算机诞生于1946年宾夕法尼亚大学,而最早的黑客却出现在麻省理工学院和贝尔实验室.初代黑客指的都是一些会计算机技术的研究人员,他们热衷于挑战,崇尚 ...

  8. 重视网络安全,华为红客联盟最新开源分享“黑客零基础入门学习路线+笔记”堪称黑客入门天花板教程

    起源 黑客这一词在莎士比亚的那个年代就存在了,最早的计算机诞生于1946年宾夕法尼亚大学,而最早的黑客却出现在麻省理工学院和贝尔实验室.初代黑客指的都是一些会计算机技术的研究人员,他们热衷于挑战,崇尚 ...

  9. 重视网络安全,红客联盟最新开源分享“黑客零基础入门学习路线+笔记”堪称黑客入门天花板教程!

    前言 黑客这一词在莎士比亚的那个年代就存在了,最早的计算机诞生于1946年宾夕法尼亚大学,而最早的黑客却出现在麻省理工学院和贝尔实验室.初代黑客指的都是一些会计算机技术的研究人员,他们热衷于挑战,崇尚 ...

最新文章

  1. java实验 输入输出流_java实验七 输入输出流
  2. Array Splitting CodeForces - 1197C
  3. linux利用patch和diff命令制作文件补丁
  4. B-树、B+树、B*树详解
  5. template 模板是怎样通过 Compile 编译的
  6. 【Flink】Flink界面如何查看数据是否倾斜
  7. php 获取域名_在PHP中截取当前页面URL地址及URL信息的方法
  8. [数据结构]前缀、中缀、前缀表达式
  9. android禁止wifi,android – 防止WiFi-direct在不使用时关闭
  10. c# 类似Excel的趋势线拟合
  11. 魔法风云纪无限元宝公益服务器,好玩网页游戏 2678魔法风云纪sf 神兽助战卡免费获得...
  12. 实验报告C语言顺序结构程序,顺序结构程序设计-C语言程序实验报告
  13. 【微信公众号开发系列文章】一、微信公众号开发环境搭建
  14. 2021年南京市高考成绩查询,2021年南京高考各高中成绩及本科升学率数据排名及分析...
  15. ios漂亮的启动动画
  16. 头歌-软件测试技术-面向对象测试_王铁军
  17. 人工智能行业每日必读(01·17)
  18. Web开发之-CS架构与BS架构
  19. 高通平台GPIO模拟PWM控制背光
  20. 浏览器全屏功能 icon图标设置

热门文章

  1. 关于HTML怎样用图片做背景
  2. 基于Matlab生成并可视化多架飞机轨迹仿真(附源码)
  3. 计算机硬盘管理 3t,3T硬盘的使用方法总结与分享
  4. 人工神经网络英文简称为,人工神经网络英文缩写
  5. 2021年低压电工考试技巧及低压电工复审模拟考试
  6. threadx也开源了
  7. MSDN WebCast网络广播全部下载列表(更新至2/11/2007)
  8. python numpy库作用_python数据分析之numpy库
  9. 递推法之-------核电站问题(超简洁代码!!!)
  10. Java-万年历的设计与实现