树网的核-Floyd+宽搜

题目:

VIJOS-P1362 树网的核

Time Limit: 1 Sec  Memory Limit: 128 MB

Description

设T=(V,  E,  W)  是一个无圈且连通的无向图(也称为无根树),每条边到有正整数的权,我们称T为树网(treebetwork),其中V,E分别表示结点与边的集合,W表示各边长度的集合,并设T有n个结点。 路径:树网中任何两结点a,b都存在唯一的一条简单路径,用d(a,  b)表示以a,  b为端点的路径的长度,它是该路径上各边长度之和。我们称d(a,  b)为a,  b两结点间的距离。 D(v,  P)=min{d(v,  u),  u为路径P上的结点}。 树网的直径:树网中最长的路径成为树网的直径。对于给定的树网T,直径不一定是唯一的,但可以证明:各直径的中点(不一定恰好是某个结点,可能在某条边的内部)是唯一的,我们称该点为树网的中心。 偏心距ECC(F):树网T中距路径F最远的结点到路径F的距离,即 ECC(F)=max{d(v,  F),v∈V} 任务:对于给定的树网T=(V,  E,  W)和非负整数s,求一个路径F,他是某直径上的一段路径(该路径两端均为树网中的结点),其长度不超过s(可以等于s),使偏心距ECC(F)最小。我们称这个路径为树网T=(V,  E,  W)的核(Core)。必要时,F可以退化为某个结点。一般来说,在上述定义下,核不一定只有一个,但最小偏心距是唯一的。 下面的图给出了树网的一个实例。图中,A-B与A-C是两条直径,长度均为20。点W是树网的中心,EF边的长度为5。如果指定s=11,则树网的核为路径DEFG(也可以取为路径DEF),偏心距为8。如果指定s=0(或s=1、s=2),则树网的核为结点F,偏心距为12。 

Input

包含n行: 第1行,两个正整数n和s,中间用一个空格隔开。其中n为树网结点的个数,s为树网的核的长度的上界。设结点编号以此为1,2,……,n。 从第2行到第n行,每行给出3个用空格隔开的正整数,依次表示每一条边的两个端点编号和长度。例如,“2  4  7”表示连接结点2与4的边的长度为7。 所给的数据都是正确的,不必检验。

Output

只有一个非负整数,为指定意义下的最小偏心距。

Sample Input

5 2
1 2 5
2 3 2
2 4 4
2 5 3

Sample Output

5
 
 
分析:
   结论:选取任意直径答案都相同
    证:设其交点为N,则两侧的每条直径权值和相等
        所以仅处理一条直径即可
     N^3做法:Floyd+宽搜
     1.建图,并Floyd
     2.找出直径,并记录直径上的点
        3.枚举直径上的起点和终点,并求出偏心距
 偏心距求法:
    偏心距可能出现在三个位置:1.直径起点到枚举起点的距离
                 2.直径终点到枚举终点的距离
                 3.枚举的路段中其他的分支
        前两个都已用Floyd算法得出,第三个分别在每个点上进行宽搜(注意,不能搜到直径上的点)
注意:1.宽搜的初始化
        2.拷贝f数组避免将其污染
    代码如下
#include<stdio.h>
#include<queue>
#include<cstring>
using namespace std;
queue<int>q;
#define N 301
#define inf 10000001
int f[N][N];
int f1[N][N];
int dis[N];
int qu[N];
int node[N][N];
int end[N];
int point[N];
int vis[N];
int cnt=1;    int n,s;
int max(int x,int y)
{
    return x>y?x:y;
}
int min(int x,int y)
{
    return x<y?x:y;
}
int bfs(int x,int cnt)
{
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=cnt;i++)
    vis[point[i]]=1;
    q.push(x);
    vis[x]=1;
    int re=0;
    while(!q.empty())
    {
        int imp=q.front();
        q.pop();
        for(int i=1;i<=n;i++)
        {
            if(vis[i]==0&&f1[i][imp]!=inf)
            {
                dis[i]=dis[imp]+f1[i][imp];
                re=max(dis[i],re);
                q.push(i);
                vis[i]=1;
            }    
        }    
    }
    return re;    
}
int main()
{

    scanf("%d%d",&n,&s);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        if(i!=j)
        f1[i][j]=f[i][j]=inf;
    }
    for(int i=1;i<n;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        f1[x][y]=f[x][y]=z;
        f1[y][x]=f[y][x]=z;
    }    
    for(int k=1;k<=n;k++)
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                f[i][j]=min(f[i][j],f[i][k]+f[k][j]);                
            }
        }
    }
    int d=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            d=max(d,f[i][j]);    
        }
    }        

    for(int i=1;i<=n;i++)
    {
        for(int j=i;j<=n;j++)
        {
            if(f[i][j]==d)
                    end[1]=i,end[2]=j;            
        }
    }
    int ans=inf;    
    for(int k=1;k<=n;k++)
    {
        if(f[end[1]][end[2]]==f[end[1]][k]+f[k][end[2]]&&k!=end[1]&&k!=end[2])
        point[++cnt]=k;    
    }
    point[1]=end[1];
    point[++cnt]=end[2];
    for(int i=1;i<=n;i++)
    dis[i]=bfs(i,cnt);
    for(int i=1;i<=cnt;i++)
    {
    int p=dis[point[i]];
        for(int j=i;j<=cnt;j++)
        {
            if(f[point[i]][point[j]]>s)
            continue;
        if(p<dis[point[j]])
            p=dis[point[j]];
            int sr=max(f[point[i]][end[1]],f[point[j]][end[2]]);
            sr=max(sr,p);
            if(sr==0)
            continue;
            ans=min(sr,ans);
//            printf("%d p:%d %d
",ans,point[i],point[j]);
        }        
    }
    printf("%d",ans);
}
   
 
原文地址:https://www.cnblogs.com/Marcelo/p/13491064.html