目录
声明:
本系列博客是《算法竞赛进阶指南》+《算法竞赛入门经典》+《挑战程序设计竞赛》的学习笔记,主要是因为我三本都买了按照《算法竞赛进阶指南》的目录顺序学习,包含书中的少部分重要知识点、例题解题报告及我个人的学习心得和对该算法的补充拓展,仅用于学习交流和复习,无任何商业用途。博客中部分内容来源于书本和网络(我尽量减少书中引用),由我个人整理总结(习题和代码可全都是我自己敲哒)部分内容由我个人编写而成,如果想要有更好的学习体验或者希望学习到更全面的知识,请于京东搜索购买正版图书:《算法竞赛进阶指南》——作者李煜东,强烈安利,好书不火系列,谢谢配合。
下方链接为学习笔记目录链接(中转站)
一、树的直径(Diameter)
树上两点的距离定义为,从树上一点到另一点所经过的权值
当树上两点距离最大时,就称作树的直径,树的直径既可以指这个权值,也可以指这个路径 (路径也叫树的最长链)。
树的直径有两种方法,都是 O ( n ) O(n) O(n)的时间复杂度。
1.树形DP求树的直径
设以1号结点为根,那么n个结点n-1条边的无向图就可以看作一个有根树。
设 D [ x ] D[x] D[x]表示从结点x 出发走向以x为根的子树,能够到达的最远结点的距离。
设x的子结点为 y 1 , y 2 . . . y t , e d g e [ x , y ] y_1,y_2...y_t,edge[x,y] y1,y2...yt,edge[x,y]表示边权
那么显然有:
D [ x ] = 1 ≤ i ≤ t m a x ( D [ y i ] + e d g e [ x , y i ] ) D[x] = ^{max}_{1≤i≤t}(D[y_i] + edge[x,y_i]) D[x]=1≤i≤tmax(D[yi]+edge[x,yi])
设 F [ x ] F[x] F[x]为经过结点x的最长链的长度。
然后就是代码:
int ans;
int vis[N];
void dp(int u){
vis[u] = 1;
for(int i = head[u];i;i = nex[i]){
int v = ver[i];
if(vis[v])continue;
dp(v);
ans = max(ans,d[u] + d[v] + edge[i]);//看这条链是不是最大的
d[x] = max(d[u],d[v] + edge[i]);//更新当前链长
}
}
2.两次BFS/DFS求树的直径
我们可以先从任意一点开始DFS,记录下当前点所能到达的最远距离,这个点为P。
在从P开始DFS记录下所能达到的最远点的距离,这个点为Q。
P , Q P,Q P,Q就是直径的端点, d i s ( P , Q ) dis(P,Q) dis(P,Q)就是直径。
具体代码见下题
1.POJ 1985.Cow Marathon(DFS求树的直径模板题)
题意:有N个农田以及M条路,给出M条路的长度以及路的方向(这道题不影响,用不到),让你找到一条 两农田(任意的)间的路径,使得距离最长,并输出最长距离。
这里用dfs求直径,当然也可以用bfs和树形DP来做。
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<bitset>
#include<vector>
#include<queue>
#define over(i,s,t) for(register int i = s;i <= t;++i)
#define lver(i,t,s) for(register int i = t;i >= s;--i)
//#define int __int128
#define lowbit(p) p&(-p)
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int INF = 0x3f3f3f3f;
const int N = 5e5+7;
const int M = 2007;
int head[N],ver[N],tot,edge[N],nex[N];
int n,m,ans;
int dis[N],vis[N];
inline void add(int u,int v,int w){
ver[++tot] = v;
edge[tot] = w;
nex[tot] = head[u];
head[u] = tot;
}
//两次dfs一次求P一次求Q
void dfs(int u,int &ed){
if(dis[u] > ans)ans = dis[u],ed = u;
vis[u] = 1;
for(int i = head[u];~i;i = nex[i]){
int v = ver[i],w = edge[i];
if(vis[v])continue;
dis[v] = dis[u] + w;
dfs(v,ed);
}
return ;
}
int p,q;
void solve(){
dfs(1,p);
ans = dis[p] = 0;
memset(vis,0,sizeof vis);
dfs(p,q);
cout<<ans<<endl;
}
int main()
{
while(scanf("%d%d",&n,&m) != EOF){
memset(head,-1,sizeof head);
memset(vis,0,sizeof vis);
memset(dis,0,sizeof dis);
tot = 0;
over(i,1,m){
int u,v,w;
char ch[2];
scanf("%d%d%d%s",&u,&v,&w,ch);
add(u,v,w);