题目链接:https://www.nowcoder.com/acm/contest/188/C
题目大意:给你n个点,他们用n-1条边无向联通,,每条边都有一个长度,给你一个初始的起点,让你起点出发,求经过每个点一次的最短路。
输入:
第一行两个整数 n,x,代表点数,和起点的位置
第二到第n行,每行三个整数 u,v,w,表示u和v之间有一条长为w的道路
输出:
一个数表示答案
思路:首先看到这道题,就想起前几天才学习的最短路,发现这个图是棵树,
然而发现这道题的规律是:以起点为根节点,除了一条从根节点到叶节点的路径只走一遍,其余的路径都要走两遍。
那么 S(最短路)=2*S(所有边)-S(从根节点到叶节点的最长路)
所以现在只要找到这条从根节点到叶节点的最长路就行了。
求从根节点到叶节点的最长路:只要从根节点开始对每一条与根节点相连的边都跑一遍,直到这个节点没有路,那么这就是一条从根节点到叶节点的路径,记录最大的路径。
这里用bfs搜索一遍就好了,
对1-2这条路,把与2相连的2-3, 2-5加入队列,标记2节点表示已经走过,并记录这条路的长度,再从队列top一条路并且pop,重复上述过程。只到这个节点没有路,那么这就是一条从根节点到叶节点的路径,记录最大的路径。等到队列为空,就把1-7加入队列,重复上述过程,一直把与1节点的路都跑一遍就找到从根节点到叶节点的最长路了。
思考:因为是第一次做最短路的题,所以用vector存邻接表,当时写完代码,一直WA,后来找到原因是存图只存了单向边,改完AC。
#include<bits/stdc++.h>
using namespace std;
struct dt{
long long to;//边的终点
long long v; //边的长度
long long s; //从起点到边的终点的总路程
}now;
vector<dt> g[50005];
stack<dt> sk;//队列
int vis[50005];
long long s1=0;//从根节点到叶节点的最长路
void bfs() //找从根节点任意一条边开始的所有路
{
while(!sk.empty())
{
now=sk.top();//得到队列的边
sk.pop();
long long m=now.to;
long long v=now.v;
vis[m]=1;//标记节点
int p=0; //记录这个节点是否还有路
for(vector<dt>:: iterator k = g[m].begin();k != g[m].end(); ++k)
{
dt now1=*k;
if(vis[now1.to]==0)//节点没有标记
{
vis[now1.to]=1;
p=1;
dt now1=*k;
now1.s += now.s;
sk.push(now1);
}
}
if(p==0)//如果这个节点没有路了,now.s就为一条从根节点到叶节点的路径
s1=max(s1, now.s);
}
}
int main()
{
int n, m;
long long s=0;
scanf("%d%d",&n,&m);
long long x, y, v;
for(int i=0;i<n-1;i++)
{
scanf("%lld%lld%lld",&x,&y,&v);
now.to=y;
now.v=v;
now.s=v;
g[x].push_back(now);//存x->y的边
now.to=x;
now.v=v;
now.s=v;
g[y].push_back(now);//存y->x的边
s+=v;
}
for(vector<dt>:: iterator k = g[m].begin();k != g[m].end(); ++k)
{
memset(vis, 0,sizeof(vis));
vis[m]=1;
sk.push(*k);//与根节点相连的边加入队列
bfs(); //找从根节点任意一条边开始的所有路
}
printf("%lld\n",s*2-s1);
return 0;
}