题目来自:HDU 3848 CC On The Tree(树上叶子结点最近点对)
题意:给定一棵树,可以知道,任意两个叶子之间都有一个距离,求距离的最小值,也就是说求隔得最近的两个叶子的最短距离是多少。
如果要求两个叶子节点之间距离的最大值,事实上这就是“树的直径”的定义。可以进行两次BFS即可。复杂度O(n)。
对于本题,对图进行分析, 可知对于任意两个叶子节点, 它们的最短路径必有共同祖先. 故对于每一个非叶子节点, 保存这个节点到离它最近的叶子节点和第二近的叶子节点的距离. 只需要进行一次dfs就可以求出所有节点到离它最近和第二近的叶子节点的距离, 复杂度为O(n),然后以O(n)的复杂度又可以更新出最优解.
下面是AC代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
using namespace std;
const int maxn=10005,inf=INT_MAX ;
int dp[maxn][2],n;
struct node
{
int to,len;
node(int to=0,int len=0):to(to),len(len){}
};
vector<node>G[maxn];
void add(int from,int to,int len)
{
G[from].push_back(node(to,len));
G[to].push_back(node(from,len));
}
void init()
{
for(int i=0;i<=n;i++) G[i].clear();
fill(&dp[0][0],&dp[maxn][0],inf);
}
void dfs(int u,int p)
{
if(G[u].size()==1)//如果u是叶子节点
{
dp[u][0]=0;//u节点到叶子节点的最短距离自然为0
return ;
}
for(int i=0;i<G[u].size();i++)//枚举所有与u相邻的节点
{
int v=G[u][i].to,len=G[u][i].len;
if(v==p) continue;
dfs(v,u);//先求出v节点到叶子节点的最短距离,以方便更新u节点到叶子节点的最短距离
if(dp[u][0]>dp[v][0]+len)//如果u节点到叶子节点的最短距离大于v节点到叶子节点的最短距离加上u-v边长,则更新
{
dp[u][1]=dp[u][0];//原来最短的变成第二短的了
dp[u][0]=dp[v][0]+len;//更新最短的
}
else if(dp[u][1]>dp[v][0]+len) dp[u][1]=dp[v][0]+len;//如果可以更新第二短的
}
}
int main()
{
//freopen("in.txt","r",stdin);
int from,to,len;
while(~scanf("%d",&n),n)
{
init();
for(int i=1;i<n;i++)
{
scanf("%d%d%d",&from,&to,&len);
add(from,to,len);
}
if(n==2)
{
printf("%d\n",len);
continue;
}
for(int i=1;i<=n;i++)
if(G[i].size()>1)//找到非叶子节点
{
dfs(i,-1);
break;
}
int ans=inf;
for(int i=1;i<=n;i++)//对非叶子节点进行选取
if(dp[i][0]&&dp[i][1]&&dp[i][0]!=inf&&dp[i][1]!=inf) ans=min(ans,dp[i][0]+dp[i][1]);
printf("%d\n",ans);
}
return 0;
}