树的直径
树的直径一般有两种求法,一是两次dfs或bfs,另一种是树形dp
两次bfs (dfs)
具体实现上,就是进行两次搜索,第一次的时候以任意的节点为根进行遍历,找到一个距离最远的点,即为直径起点,第二次的时候以该起点为根进行搜索,再找到距离最远的点,即为直径的终点
注:两次搜索的处理方法可以有效地寻找直径的具体路径,但是无法处理负边权的情况
//dfs程序
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<math.h>
#include<vector>
#include<queue>
#define ll long long
inline ll read()
{
ll x=0,f=1;
char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-') f=-1;
ch=getchar();
}
while(isdigit(ch))
{
x=(x<<1)+(x<<3)+ch-'0';
ch=getchar();
}
return x*f;
}
const ll maxn=1e4+10;
const ll inf=1e10+10;
ll n,ans,maxx=-inf,st,en;
ll dis[maxn],vis[maxn];
std::vector<std::pair<ll,ll> > e[maxn];
inline void dfs(ll x)
{
vis[x]=1;
for(int i=0;i<e[x].size();i++)
{
ll y=e[x][i].first;
if(!vis[y])
{
dis[y]=dis[x]+e[x][i].second;
dfs(y);
}
}
}
int main(void)
{
n=read();
for(int i=1;i<=n-1;i++)
{
ll x=read(),y=read(),z=read();
e[x].push_back(std::make_pair(y,z));
e[y].push_back(std::make_pair(x,z));
}
for(int i=1;i<=n;i++) dis[i]=inf;
std::memset(vis,0,sizeof(vis));
dis[1]=0;
dfs(1);
for(int i=1;i<=n;i++)
{
if(dis[i]>maxx&&dis[i]!=inf)
{
maxx=dis[i];
st=i;
}
}
for(int i=1;i<=n;i++) dis[i]=inf;
std::memset(vis,0,sizeof(vis));
dis[st]=0;
dfs(st);
maxx=-inf;
for(int i=1;i<=n;i++)
{
if(dis[i]>maxx&&dis[i]!=inf)
{
maxx=dis[i];
en=i;
}
}
printf("%lld\n",maxx);
}
//bfs程序
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<math.h>
#include<vector>
#include<queue>
#define ll long long
#define db double
const ll maxn=1e4+10;
const db inf=1e9+10;
ll n,st,en,tot;
ll ans,maxx;
ll dis[maxn],vis[maxn],pre[maxn],used[maxn],head[maxn];
std::vector<std::pair<ll,ll> > e[maxn];
std::queue<ll> q;
struct node
{
ll u,v,w,nxt;
} s[maxn];
inline void add(ll u,ll v,db w)
{
s[++tot].v=v;
s[tot].w=w;
s[tot].nxt=head[u];
head[u]=tot;
}
inline void bfs(ll x)
{
pre[x]=0;
vis[x]=1;
q.push(x);
while(q.size())
{
ll u=q.front();
q.pop();
for(int i=0;i<e[u].size();i++)
{
ll v=e[u][i].first;
db w=e[u][i].second;
if(!vis[v])
{
pre[v]=u;
dis[v]=dis[u]+w;
vis[v]=1;
q.push(v);
}
}
}
}
inline void zj()
{
for(int i=1;i<=n;i++) dis[i]=inf;
std::memset(vis,0,sizeof(vis));
dis[1]=0;
bfs(1);
maxx=-1;
for(int i=1;i<=n;i++)
{
if(dis[i]>maxx&&dis[i]!=inf)
{
st=i;
maxx=dis[i];
}
}
for(int i=1;i<=n;i++) dis[i]=inf;
memset(vis,0,sizeof(vis));
dis[st]=0;
bfs(st);
maxx=-1;
for(int i=1;i<=n;i++)
{
if(dis[i]>maxx&&dis[i]!=inf)
{
en=i;
maxx=dis[i];
}
}
ans+=maxx;
}
int main(void)
{
scanf("%lld",&n);
for(int i=1;i<=n-1;i++)
{
ll x,y;
db z;
scanf("%lld %lld %lf",&x,&y,&z);
e[x].push_back(std::make_pair(y,z));
e[y].push_back(std::make_pair(x,z));
}
zj();
printf("%lld\n",ans);
return 0;
}
DP求解
一条路径可看为路径中一边与该边两端点到对应直径端点的距离和,在DP中处理最大值,并维护最长链即可
注:DP可以处理存在负边权的情况,但是不能处理出直径的具体方案
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<math.h>
#include<vector>
#include<queue>
#define ll long long
inline ll read()
{
ll x=0,f=1;
char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-') f=-1;
ch=getchar();
}
while(isdigit(ch))
{
x=(x<<1)+(x<<3)+ch-'0';
ch=getchar();
}
return x*f;
}
const ll maxn=1e5+10;
ll n,ans=-maxn*maxn,tot;
ll d[maxn],vis[maxn],head[maxn];
struct node
{
ll v,w,nxt;
} s[maxn];
inline void add(ll u,ll v,ll w)
{
s[++tot].v=v;
s[tot].w=w;
s[tot].nxt=head[u];
head[u]=tot;
}
inline void dp(ll x)
{
vis[x]=1;
for(int i=head[x];i;i=s[i].nxt)
{
ll y=s[i].v;
ll w=s[i].w;
if(vis[y]) continue;
dp(y);
ans=std::max(ans,d[x]+d[y]+w);
d[x]=std::max(d[x],d[y]+w);
}
}
int main(void)
{
n=read();
for(int i=1;i<=n-1;i++)
{
ll x=read(),y=read(),z=read();
add(x,y,z);
add(y,x,z);
}
dp(1);
printf("%lld\n",ans);
return 0;
}