Topcoder SRM 635 Div2 1000 (一种 O(n) 求一棵树中最长连续边 长度的方法)


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.
The distance between those vertices is the sum of lengths of edges on that simple path.
The diameter of the tree is the maximum of all those pairwise distances.


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

 
Class:LonglongestPathTree
Method:getLength
Parameters:vector <int>, vector <int>, vector <int>
Returns:long long
Method signature:long long getLength(vector <int> A, vector <int> B, vector <int> L)
(be sure your method is public)

Limits

 
Time limit (s):2.000
Memory limit (MB):256
Stack limit (MB):256

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) 
 
{0,0,0}
{1,2,3}
{2,4,8}
Returns: 14
The tree has 4 vertices and 3 edges: 1-0 (length 2), 2-0 (length 4) and 0-3 (length 8). Currently, the farthest pair of vertices is(2,3); their distance is 8+4=12.
If we remove the edge 1-0 and add an edge 3-1, we'll get a tree with edges2-0 (length 4), 0-3 (length 8) and 3-1 (length 2). Now, the fathest pair of vertices is(2,1); their distance is 8+4+2=14.
Obviously, we can't do better than that (but this is not the only way to achieve distance 14).
1) 
 
{0,1,2,3}
{1,2,3,4}
{1,2,3,4}
Returns: 10
One optimal solution is not changing the tree.
2) 
 
{0,1,0,3,0,6,7,7,8,9,11}
{1,2,3,4,5,5,5,8,9,10,9}
{100,1000,100,1000,1,10,10,10,10,100,100}
Returns: 2410
3) 
 
{1,5,6,4,4,0,3,3}
{6,6,4,8,0,3,2,7}
{1,1,1,1,1,1,1,1}
Returns: 7
4) 
 
{0,1,2,3,0,1,2,3,4}
{1,2,3,4,5,6,7,8,9}
{10,1,1,10,10,1000,100,1000,10}
Returns: 2122

注:原题中还有第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;
    }
};





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值