noip2007 树网的核

描述

      设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。


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


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

    8 6
    1 3 2
    2 3 2
    3 4 6
    4 5 3
    4 6 4
    4 7 2
    7 8 3

样例输出

    5

    5

提示
    40%的数据满足:5<=n<=15

    70%的数据满足:5<=n<=80

    100%的数据满足:5<=n<=300,0<=s<=1000。边长度为不超过1000的正整数


题意:就是找到图中最长的"路径"(可能不止一条),称之为直径。在直径上找到任意两点,称之为核。图上任意一点到核的距离称为偏心距。
求最小的偏心距。
算法:预处理出任意两点间的距离dist<i,j>,再找到直径。将直径上的所有点放入pst数组。
最后用两层循环分别枚举直径上的两个点求最小ecc。

90分代码:

#include<queue>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#define MAXN 500
#define INF 1000007
using namespace std;
int n,s;
int net[MAXN][MAXN],dist[MAXN][MAXN],dis[MAXN];
int vis[MAXN],path[MAXN];
int pts[MAXN],sum=0;

void read(){
    cin >> n >> s;
    int u,v,w;
    memset(net,0,sizeof net);
    for (int i=1;i<n;i++)
    {
        scanf("%d%d%d",&u,&v,&w);
        net[u][v]=net[v][u]=w;
    }
}
void spfa(int sc)
{
    queue<int> Q;
    for (int i=1; i<=n; i++) dis[i] = INF;
    dis[sc] = 0;
    vis[sc] = 1;
    Q.push(sc);
    while (!Q.empty()){
        int tmp = Q.front();
        for (int i=1; i<=n; i++)
        {
            if (net[tmp][i] && dis[tmp] + net[tmp][i] < dis[i])
            {
                dis[i] = dis[tmp] + net[tmp][i];
                if (!vis[i])
                {
                    Q.push(i);
                    vis[i] = 1;
                }
            }
        }
        Q.pop();
        vis[tmp] = 0;
    }
    for (int i=1;i<=n;i++) dist[sc][i]=dist[i][sc]=dis[i];
}

void dfs(int u,int v,int k){
    path[k]=u;
    if (u==v) for (int i=1;i<=k;i++) pts[path[i]]=1;
    else for (int i=1;i<=n;i++)
        if (net[u][i] && !vis[i])
    {
        vis[i]=1;
        dfs(i,v,k+1);
    }
}
void init(){
    read();
    for (int i=1;i<=n;i++) spfa(i);//用spfa预处理出两点间距离
    for (int i=1;i<=n;i++) dist[i][i]=0;

    int maxv=0,u[MAXN],v[MAXN],cnt=0;

    for (int i=1;i<n;i++)
        for(int j=i;j<=n;j++) maxv=max(maxv,dist[i][j]);
    for (int i=1;i<=n;i++)
        for(int j=i;j<=n;j++) if (dist[i][j]==maxv) {u[++cnt]=i;v[cnt]=j;}
    //直径可能不止一条
    for (int i=1;i<=cnt;i++)
    {
        memset(vis,0,sizeof vis);
        vis[u[i]]=1;
        dfs(u[i],v[i],1);
	//找直径上的所有点
    }
}

bool check(int u,int v){
    return pts[u]&&pts[v];
}
//判断:是否在直径上
int ecc(int u,int v){
    int ECC=0;
    for (int i=1;i<=n;i++) {
        int d=min(dist[i][u],dist[i][v]);
        ECC=max(ECC,d);
    }
    return ECC;
}
//计算偏心距
int main(){
    //freopen("data.in","r",stdin);
    init();
    int ans=INF;
    for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++)
    if (check(i,j)&& dist[i][j]<=s)  ans = min(ans,ecc(i,j));
    cout<<ans;
    return 0;
}






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值