Problem Statement | |||||||||||||
There is a weighted tree with N vertices. The vertices are numbered 0 through N-1. You're given a description of the tree in three vector <int>s: A,B and L. For each 0 <= i <= N-2, there's an edge between verticesA[i] and B[i]; the length of this edge isL[i]. In a tree, each pair of vertices is connected by exactly one simple path. You're allowed to remove one of the edges and then add another edge. There are two constraints: the length of this edge must be the same as the length of the removed edge and the resulting graph must again be a tree. Compute and return the maximum diameter of the resulting tree that can be achieved this way. | |||||||||||||
Definition | |||||||||||||
| |||||||||||||
Limits | |||||||||||||
| |||||||||||||
Constraints | |||||||||||||
- | N will be between 2 and 2,000, inclusive. | ||||||||||||
- | Arrays A, B and L will each containN-1 elements. | ||||||||||||
- | Each element of A and B will be between0 and N-1, inclusive. | ||||||||||||
- | Each element of L will be between 1 and 1,000,000,000, inclusive. | ||||||||||||
- | A and B will describe a tree. | ||||||||||||
Examples | |||||||||||||
0) | |||||||||||||
| |||||||||||||
1) | |||||||||||||
| |||||||||||||
2) | |||||||||||||
| |||||||||||||
3) | |||||||||||||
| |||||||||||||
4) | |||||||||||||
|
注:原题中还有第5个栗子,由于太多太长,就不放出来溜了
这题,让我学到了一个知识。
给定一棵树,求树中最长枝的长度。怎么办?算法复杂度是多少?
我说的不抽象吧?最长枝指的就是最长的连续边。可以在O(n)时间内求出正确答案。n代表树中点的个数
没必要对每个点都dfs一次。只需要取定一个起点dfs即可。为什么?
我们想,树是连通的,无环的。假设我们现在有一个最长枝在树中。这个最长枝一定有一个折点。折点什么意思?就是指拐弯点。
那么,我们取定一个顶点去搜索,一定可以经过这个折点。那么最长枝长度就是 可到达当前点的点 到当前点距离的 最长值+次长值。
(如果没有次长值,次长值就是0,没有最长值,最长值就是0).
在思考这个算法时候,遇到了一个问题。这里有点不太好讲。(听不懂别怪我哈,表达能力不好)
指的是,只进行一次dfs,对于当前点,我可以处理除了从父亲节点过来的那条边以外的所有边 所能接触到的点 到达 当前点的最长距离。
但是,父亲节点过来的那条边的最长距离就不好处理了,甚至不能处理。
这就相当于,我现在在求多源最长路径,真的一遍dfs就可以处理了吗?
实际上是可以的。具体为什么,我就不文字阐述了。懂的自然懂,要讲述有图才好说。
那么,既然可以在O(n)复杂度内求得一个棵树中最长枝的长度。接下来就好办了。
我枚举每一条边,把这条边相当于打断。是不是一棵树就变成了两棵树?我把它命名为左树和右树。
左树和右树,用上述的方法,求出每棵树里最长枝的长度。
那么,就相当于把这条枝打断,在通过这条枝拼接。拼接后最大值就是 左树最长枝长度+当前枝长度+右树最长枝长度
即ans=max(ans,左树最长枝长度+当前枝长度+右树最长枝长度)
枚举每条边,维护这个最大值,就可以得到最终答案了。
//Hello. I'm Peter.
#include<cstdio>
#include<iostream>
#include<sstream>
#include<cstring>
#include<string>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<functional>
#include<cctype>
#include<ctime>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double ld;
#define peter cout<<"i am peter"<<endl
#define input freopen("data.txt","r",stdin)
#define randin srand((unsigned int)time(NULL))
#define INT (0x3f3f3f3f)*2
#define LL (0x3f3f3f3f3f3f3f3f)*2
#define gsize(a) (int)a.size()
#define len(a) (int)strlen(a)
#define slen(s) (int)s.length()
#define pb(a) push_back(a)
#define clr(a) memset(a,0,sizeof(a))
#define clr_minus1(a) memset(a,-1,sizeof(a))
#define clr_INT(a) memset(a,INT,sizeof(a))
#define clr_true(a) memset(a,true,sizeof(a))
#define clr_false(a) memset(a,false,sizeof(a))
#define clr_queue(q) while(!q.empty()) q.pop()
#define clr_stack(s) while(!s.empty()) s.pop()
#define rep(i, a, b) for (int i = a; i < b; i++)
#define dep(i, a, b) for (int i = a; i > b; i--)
#define repin(i, a, b) for (int i = a; i <= b; i++)
#define depin(i, a, b) for (int i = a; i >= b; i--)
#define pi 3.1415926535898
#define eps 1e-6
#define MOD 1000000007
#define MAXN
#define N 2014
#define M
struct Edge
{
int to,next,from;
ll val;
}edge[10*N];
int head[N],w;
void add_edge(int from,int to,ll val)
{
w++;
edge[w].from=from;
edge[w].to=to;
edge[w].val=val;
edge[w].next=head[from];
head[from]=w;
}
int n;
bool vis[N];
ll anst,myval[N];//myval[i]用于dfs搜索,表示最远点到i这个点的最长距离。
void dfs(int now)
{
vis[now]=true;
int i,to;
ll val;
ll firstbig=0,secondbig=0,big;
//firstbig表示点到now这个点的最长长度
//secondebig表示...次长长度
//假设以now节点为折点,当前最长枝长度为firstbig+secondbig
for(i=head[now];i!=-1;i=edge[i].next)
{
to=edge[i].to;
if(vis[to]) continue;
val=edge[i].val;
dfs(to);
big=val+myval[to];//距离为子节点的最大值+当前这条边的距离
//下面每次维护最大值和次大值
if(big>firstbig) secondbig=firstbig,firstbig=big;
else if(big>secondbig) secondbig=big;
}
//每次维护最远点到当前点的最远距离,以及维护以当前点为折点时候最长枝的长度
myval[now]=firstbig;
anst=max(anst,firstbig+secondbig);
}
class LonglongestPathTree
{
public:
long long getLength(vector <int> A, vector <int> B, vector <int> L)
{
int len=gsize(A);
clr(edge);
clr_minus1(head);
w=0;
int from,to;
ll val;
//先建图,无向图
rep(i,0,len)
{
from=A[i],to=B[i],val=(ll)L[i];
add_edge(from,to,val);
add_edge(to,from,val);
}
n=len+1;
ll anst1,anst2,ans=0;
//对于每条边,分别dfs搜索它的左树和右树中最长连枝的长度
//对于一棵树,在O(n)时间内就可以得到最长连枝的长度。n为树上点的个数
//当前答案就是左树最长连枝长度+当前边长度+右树最长连枝长度。每次维护答案最大值即可。
repin(i,1,w)
{
from=edge[i].from;
to=edge[i].to;
val=edge[i].val;
rep(j,0,n)
{
vis[j]=false;
}
vis[from]=true;
anst=0;
dfs(to);
anst1=anst;
rep(j,0,n)
{
vis[j]=false;
}
vis[to]=true;
anst=0;
dfs(from);
anst2=anst;
ans=max(ans,anst1+val+anst2);
}
return ans;
}
};