题目:
有多个城市与王城相连,从王城到每个城市有且仅有一条路径(代表这是一个无环图,是一颗树的结构),每条路径都有长度作为它的权值,有这样一条收费规则:每从X km到X+1 km处,这1km需要花费(X+1)+10的钱。现在问有一个大臣要从某一个城市到另一个城市,问你最多需要多少钱。
样例:
输入:
第一行代表几个城市;
下面n-1行的3个数分别表示城市x到y之间有一条权值为z的路径(双向)
5
1 2 2
1 3 1
2 4 5
2 5 4
输出:
最后的费用:
135
分析:
首先,钱的花费明显与距离有直接的关系,走的路越长,就花的越多。那么我们就是要在一个图中去找到一条最长路。那么最长路也就是一颗树的直径,有一个方法可以算树的直径(直径可能不止一条,但这样求出来的绝对是其中一条):先从任意点a到离a最远的点b,再从b到离他最远的点c,那么b到c的距离就是最长的路径(树的直径之一)。原理可以用反证法,假设有另一条边才是最长边比b到c还长,无论什么情况下,都可以证明这条边不是最长边(都能找到一个更长的,所以矛盾)。
那么最长路求出来了,最后就是一个与路长有关的费用公式推理了,由于最后长度可能比较长,每次用循环推肯定是不行的。
费用公式推理:
首先:有题目可知从
0->1:1+10=11
0->2:2+10+1+10=23
0->3:3+10+2+10+1+10=36
那么很快规律就出来了,总长1km时费用为1+1*10;2km时为:1+2+2*10;3km时为:1+2+3+3*10;
那么容易得到长度:
n km时:1+2+3+...+n-1+n+n*10;(一个n倍10加上一个从1到n的等差数列前n项和)
等差数列前n项和的公式为:(a1+an)*n/2;(a1:首项;an:末项;n:项数)
所以n km的花费为:ans = n*10+(1+n)*n/2;
验算一下公式的正确性,由样例可得最长为9km,待入公式n中
ans=90+45=135(完全正确!)
贴代码!!!:
#include<iostream>
#include<algorithm>
#include<queue>
#include<utility>
#include<cstring>
#include<stack>
#include<cmath>
using namespace std;
typedef long long llint;
const int N=1e5+5;
llint n,node=0,head[N],vis[N];
struct Node{
llint to,pre,weight;
}edge[2*N];
//结构体储存边:到哪里去;父亲在哪里;权值是多少。
void add(llint u,llint v,llint w){
edge[node].to=v;
edge[node].weight=w;
edge[node].pre=head[u];
head[u]=node++;
}
//链式前向星建边存图
llint ans,sum,str;//ans装最终最长的那条边,sum累加每条路径的长度来不断跟新ans,str是第一次dfs最远点的那个点的载体。
void dfs(llint u){
if(ans<sum){
str=u;
ans=sum;//每次找到更远的点就跟新ans和str
}
for(llint i=head[u];i!=-1;i=edge[i].pre){//链式前向星遍历u的出边
llint v=edge[i].to;
llint w=edge[i].weight;
if(!vis[v]){//没被走过
vis[v]=1;//进入dfs
sum+=w;
dfs(v);
sum-=w;
vis[v]=0;
}
}
return;
}
int main(){
cin >> n;
memset(head,-1,sizeof head);//链式前向星初始化
for(llint i=1;i<n;i++){
llint u,v,w;
scanf("%lld%lld%lld",&u,&v,&w);
add(u,v,w);//链式前向星存图
add(v,u,w);//双向存图
}
ans=0;//记录答案
sum=0;//用来记录当前路径上的权值
vis[1]=1;//任选一点当作起点开始dfs树找离他最远的点
dfs(1);
vis[1]=0;//找到了后回来依次删除标记,因为后面str还要来一次dfs找最远
ans=0;//重新初始化,因为递归回来的时候依次讲标记撤销了,所以vis不需要初始化
sum=0;
vis[str]=1;//第二次dfs最远点,找到最终长度。
dfs(str);
cout << ans*10+(ans+1)*ans/2 << endl;//运用公式代入最终长度计算答案。
return 0;
}