Codeforces Beta Round #14 (Div. 2) D. Two Paths

题目链接

题目大意

给定一棵无向树。从中选择两条不相交的路径,使得两条路径的长度乘积最大。
路径不相交定义为两个路径没有公共点。

题解

首先可以想到一个O(n^4)的算法,就是枚举4个点,也就是两条路径,然后判断是否可行。
考虑如何优化,可以先用n^2的复杂度枚举一条路径,然后贪心得去求另一条路径,另一条路径当然越大越好了。
而从树中删掉一条路径,剩下的就是森林了,在每棵树上求一下直径,求出最大值,就可以更新答案了。
这题的数据范围是n<=200,所以O(n^3)足以过掉这道题了。

#include
#include
using namespace std;
const int M=205;
struct Edge{
    int to,nxt;
}edge[M<<1];
int T,head[M];
void add_edge(int a,int b){
    edge[T]=(Edge){b,head[a]};
    head[a]=T++;
}
int fa[M],dep[M];
int mx,ID,ans,n;
bool mark[M],flag[M];
void rec(int x,int f,int d){
    if(!dep[x]) fa[x]=f,dep[x]=d;
    mark[x]=1;
    if(mxfor(int i=head[x];~i;i=edge[i].nxt){
        if(flag[edge[i].to]||edge[i].to==f) continue;
        rec(edge[i].to,x,d+1);
    }
}
int LCA(int a,int b){
    if(dep[a]while(dep[a]>dep[b]) flag[a]=1,a=fa[a];
    while(a!=b){
        flag[a]=1;
        flag[b]=1;
        a=fa[a],b=fa[b];
    }
    flag[a]=flag[b]=1;
    return a;
}
void solve(int a,int b){
    for(int i=1;i<=n;i++)
        flag[i]=mark[i]=0;
    int lca=LCA(a,b),l=0;
    for(int i=1;i<=n;i++)
        if(!mark[i]){
            mx=-1;rec(i,0,0);
            mx=-1;rec(ID,0,0);
            l=max(l,mx);
        }
    ans=max(ans,l*(dep[a]+dep[b]-dep[lca]*2));
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        head[i]=-1;
    for(int i=1;iint a,b;
        scanf("%d%d",&a,&b);
        add_edge(a,b);
        add_edge(b,a);
    }
    rec(1,0,1);
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
            solve(i,j);
    printf("%d\n",ans);
    return 0;
} 

然而这还不算完,还有O(n^2)的做法。
由于这棵树是联通的,所以选完了两条路径之后肯定存在第三条路径连接着两条路径。
于是枚举一条边,然后树就被分成了两棵树,求出两棵树的直径的最大值,然后就可以更新答案了。

#include
#include
using namespace std;
const int M=205;
struct Edge{
    int to,nxt;
}edge[M<<1];
int head[M],T,del,len[2];
void add_edge(int a,int b){
    edge[T]=(Edge){b,head[a]};
    head[a]=T++;
}
int mx,ID;
void rec(int x,int f,int dep){
    if(dep>mx){
        mx=dep;
        ID=x;
    }
    for(int i=head[x];~i;i=edge[i].nxt){
        if(edge[i].to==f||del==(i>>1)) continue;
        rec(edge[i].to,x,dep+1);
    }
}
void solve(int x,int f){
    mx=-1;
    rec(x,0,0);
    rec(ID,0,0);
    len[f]=mx;
}
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        head[i]=-1;
    for(int i=1;iint a,b;
        scanf("%d%d",&a,&b);
        add_edge(a,b);
        add_edge(b,a);
    }
    int ans=0;
    for(int i=0;i1;i++){
        del=i;
        solve(edge[i<<1].to,0);
        solve(edge[i<<1|1].to,1);
        ans=max(ans,len[0]*len[1]);
    }
    printf("%d\n",ans);
    return 0;
}

思考

如果可以选择k条路径,那可以怎么写呢?

你可能感兴趣的:(codeforces,图论,——树的直径)