【noip2007】树网的核

15 篇文章 0 订阅
6 篇文章 0 订阅

题目描述

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

这里写图片描述

输入输出格式

输入格式:
输入文件core.in包含n行:

第1行,两个正整数n和s,中间用一个空格隔开。其中n为树网结点的个数,s为树网的核的长度的上界。设结点编号以此为1,2,……,n。

从第2行到第n行,每行给出3个用空格隔开的正整数,依次表示每一条边的两个端点编号和长度。例如,“2 4 7”表示连接结点2与4的边的长度为7。

输出格式:
输出文件core.out只有一个非负整数,为指定意义下的最小偏心距。

输入输出样例

输入样例#1: 复制
5 2
1 2 5
2 3 2
2 4 4
2 5 3

输出样例#1: 复制
5
输入样例#2: 复制
8 6
1 3 2
2 3 2
3 4 6
4 5 3
4 6 4
4 7 2
7 8 3
输出样例#2: 复制
5
说明

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

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

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

NOIP 2007 提高第四题

终于能够独立AC T4 了,3.5h惊天大模拟;
本蒟蒻看不出算法,只好模拟+暴力+最短路;
没想到竟然过了
简要说下思路:
一:跑个Floyed求出两点之间的距离(好像有点浪费,但数据范围这么小,就瞎写吧。。)
二:跑bfs求出图中最长的路径(即题中的直径),实现方法上:建一个结构体Node 存储点的 id(编号),dis(距离起点的距离),step(到达这个点时走的步数),pre(这个点的前驱),然后一起扔到队列里。与当前最优值更新存到road里即可;
在这里我是选取出度为1的点广搜的,小贪心,很容易理解吧
三:接下来就是暴力的不能再暴力了
枚举直径上所有的合法的路径,对于每一条路径枚举所有的点到这条路径的最小值,在这些里面选一个最大值(最远距离)。最后取所有的路径中的最小值(最小的最大偏心距)。输出即可。
Code:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
int n,s,chu[301];
struct Edge{
    int to,next,dis;
}edge[1001];
bool b[301];
struct Node{
    int id,dis,step,pre;
}node[301];
queue<Node> que;
int maxstep; int nowmax=0;//nowmax不能放在spfa里面啊 
int f[301][301];
int ans=0x7fffffff;

int head[301],num_edge;
void add_edge(int from,int to,int dis)
{
    edge[++num_edge].next=head[from];
    edge[num_edge].to=to;
    edge[num_edge].dis=dis;
    head[from]=num_edge;
}

int maxdis; int road[301],tot;
void dfs(int x,int step,int dis)//这是用深搜写的求直径的方法,但是不知道为什么WA了几个点,大佬们帮我看看也好 
{
    if (step==1) road[1]=x;
    if (dis>maxdis)
    {
        maxdis=dis; road[step]=x; maxstep=step;
    }
    b[x]=true;
    for (int i=head[x]; i!=0; i=edge[i].next)
    {
        if (!b[edge[i].to])
        {
            dfs(edge[i].to,step+1,dis+edge[i].dis);
        }
    }
}

void spfa(int x)
{
    memset(b,0,sizeof(b));//不要忘记每次清零 
    memset(node,0,sizeof(node));
    b[x]=true;//起点标记 
    for (int i=1; i<=n; i++) node[i].id=i;

    node[x].dis=0; node[x].step=1;
    que.push(node[x]);
    while (!que.empty())
    {
        Node now=que.front(); que.pop();
        int t=0;
        for (int i=head[now.id]; i!=0; i=edge[i].next)
        {
            if (!b[edge[i].to])
            {
                t++;
                b[edge[i].to]=true;
                node[edge[i].to].dis=now.dis+edge[i].dis;
                node[edge[i].to].pre=now.id;
                node[edge[i].to].step=now.step+1;
                que.push(node[edge[i].to]);
            }
        }
//      if (nowmax<now.dis&&!t)//从终点(没有出度)向前保存路径 
        if (nowmax<now.dis&&chu[now.id]==1&&now.id!=x)
        {
            nowmax=now.dis;
            maxstep=now.step;
            road[now.step]=now.id;
            int p=now.pre;
            while (p)
            {
                road[node[p].step]=node[p].id; p=node[p].pre;
            }
        }
    }
}

void Floyed()
{
    for (int k=1; k<=n; k++)
        for(int i=1; i<=n; i++)
            for (int j=1; j<=n; j++)
//              if (i!=j&&j!=k&&i!=k)
                    f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
/*  for (int i=1; i<=n; i++)
    {
        for(int j=1; j<=n; j++) printf("%d ",f[i][j]);
        printf("\n");
    }*/
}

bool vis[301],used[301]; int max_dis;
void work(int x,int y)
{
    int road_max=0;
    for (int i=1; i<=n; i++)
    {
        int node_min=0x7fffffff;
        for (int j=x; j<=y; j++)
        {
//          if (i!=road[j])
            node_min=min(node_min,f[i][road[j]]);
        }
        road_max=max(road_max,node_min);
    }
    ans=min(ans,road_max);
}

int main()
{
    scanf("%d%d",&n,&s);
    memset(f,0x7f/3,sizeof(f));
    for (int i=1; i<=n; i++) f[i][i]=0;
    for (int i=1; i<=n-1; i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        add_edge(x,y,z); add_edge(y,x,z);
        f[x][y]=f[y][x]=z;
        chu[x]++; chu[y]++;
    }
    Floyed();
    for (int i=1; i<=n; i++)
        if (chu[i]==1)
        {
//          dfs(i,1,0);
            spfa(i);
        }
/*  int maxn=0; int k;
    for (int i=head[road[2]]; i!=0; i=edge[i].next)
        if (maxn<edge[i].dis) {maxn=edge[i].dis; k=edge[i].to;}
    road[1]=k;
    for (int i=1; i<=maxstep; i++) printf("%d ",road[i]);*/
    for (int i=1; i<=maxstep; i++)
    {
        for (int j=i; j<=maxstep; j++)
        {
            if (f[road[i]][road[j]]>s) break;
                work(i,j);
        }
    }
    printf("%d",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值