思路:
求某个点使得它到图中的所有点的最大距离最小。
首先直接找图的直径是不对的,比如环上等距的三个点,然后其中一个点往外扩展了一个点,由于我求出来的点之后按理要放在直径的中点上,但这种情况显然不满足。
对于这种带了环的题目(又叫基环外向树),我们一般是断掉环上的某一条边,再作考虑。
把环上的某一条边断掉变成树再求直径就没问题了。
首先外送快餐店送餐的路径不会经过完整的一个环,所以断边对正确答案没有影响。因为环上的点有两个条路径,所以删掉不会导致“错过正确答案”。
先跑一遍dfs,找出环上的点编号(环上有top条边),以及点i和i+1的边的编号是i,点top和1的边编号top。
我们不知道断掉那条边最优,所以需要枚举断掉边
先断掉边top(环上边的编号,链接环上的第top个点和第1个点)
对环上每个点的子树跑一遍dfs得到dp[i]表示环上的第i个点子树的深度(同时求出所有子树内最大链长度)
然后求出表示环上的点
的最大深度,last[i][0]表示经过环上的点
中至少一个点的最大链长度
以及求出表示环上的点
的最大深度,last[i][1]表示经过环上的点
中至少一个点的最大链长度
断掉边top的最大链长度(需要经过环上的点)就是last[top][0];
断边i后的最大链(需要经过环上的点)可能是或
的最大链(需要经过环上的点),也可能是
和
中深度最大的链相连(需要经过环上的点)。
长度最大的链可能不经过环上的点,所以最后还要和所有子树内最大链长度比较。
Code:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+7,maxm=2e5+7;
int head[maxn],Next[maxm],to[maxm],fa[maxn],tot,b[maxn],top;
bool vis[maxn],sccno[maxn];
ll val[maxm],pre[maxn][2],last[maxn][2],fa_w[maxn],edge[maxn],dp[maxn],chain;
inline ll read() {
ll s = 0, w = 1;
char ch = getchar();
while (ch < 48 || ch > 57) {
if (ch == '-') w = -1;
ch = getchar();
}
while (ch >= 48 && ch <= 57)
s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
return s * w;
}
void add(int x,int y,ll v) {
to[++tot]=y;
Next[tot]=head[x];
head[x]=tot;
val[tot]=v;
}
void dfs_find(int x,int f) {
vis[x]=true;
for(int i=head[x],y;i;i=Next[i]) {
y=to[i];
if(y==f||sccno[y]) continue;
if(vis[y]) {
int u=x;
while(u!=y) {
sccno[u]=true;
b[++top]=u,edge[top]=fa_w[u];
u=fa[u];
}
sccno[y]=true;
b[++top]=y,edge[top]=val[i];
continue;
}
fa[y]=x,fa_w[y]=val[i];
dfs_find(y,x);
}
}
void dfs(int x,int f) {
for(int i=head[x],y;i;i=Next[i]) {
y=to[i];
if(y==f||sccno[y]) continue;
dfs(y,x);
chain=max(chain,dp[x]+dp[y]+val[i]);
dp[x]=max(dp[x],dp[y]+val[i]);
}
}
int main() {
int n=read();
for(int i=1,u,v,cost;i<=n;++i) {
u=read(),v=read(),cost=read();
add(u,v,cost);
add(v,u,cost);
}
dfs_find(1,0);
for(int i=1;i<=top;i++) dfs(b[i],0);
ll sum=0,plus=0,duan=edge[top];
for(int i=1,now;i<=top;++i) {
sum+=edge[i-1];
now=b[i];
pre[i][0]=max(pre[i-1][0],dp[now]+sum);
last[i][0]=max(last[i-1][0],dp[now]+plus+sum);
plus=max(plus,dp[now]-sum);
}
edge[top]=sum=plus=0;
for(int i=top,now;i;--i) {
sum+=edge[i];
now=b[i];
pre[i][1]=max(pre[i+1][1],dp[now]+sum);
last[i][1]=max(last[i+1][1],dp[now]+plus+sum);
plus=max(plus,dp[now]-sum);
}
ll ans=last[top][0],choose=0;
for(int i=1;i<top;++i) {
choose=max(last[i][0],last[i+1][1]);
choose=max(choose,pre[i][0]+pre[i+1][1]+duan);
if(choose<ans) ans=choose;
}
ans=max(ans,chain);
if(ans&1) printf("%lld.5",ans>>1);
else printf("%lld.0",ans>>1);
return 0;
} 
京公网安备 11010502036488号