NOIP2007 树网的核 [dfs] [数据结构] [树的直径]

21 篇文章 0 订阅
7 篇文章 0 订阅

树网的核
(core.pas/c/cpp)
【问题描述】
设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的距离,即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】
core.in Core.out
5 2
1 2 5
2 3 2
2 4 4
2 5 3 5
【输入输出样例2】
core.in core.out
8 6
1 3 2
2 3 2
3 4 6
4 5 3
4 6 4
4 7 2
7 8 3 5
【限制】
40%的数据满足:5<=n<=15
70%的数据满足:5<=n<=80
100%的数据满足:5<=n<=300,0<=s<=1000。边长度为不超过1000的正整数


Floyed的O(n^3)太暴力,但是考虑到是07年的题,但是肯定过不了。。
那么O(n^2)的做法枚举直径上的子路径,然后求最大深度。
O(nlogn)的话,我用优先队列维护当前最大可以优化的路径,那么维护dis数组,表示当前子树的最长路径。
网上看到另一种做法,也是O(nlogn),先找出直径,然后对于直径上的点可以用数据结构来维护。。
我的做法:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<iomanip>
#include<ctime>
#include<climits>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
#define smax(x,tmp) x=max((x),(tmp))
#define smin(x,tmp) x=min((x),(tmp))
#define maxx(x1,x2,x3) max(max(x1,x2),x3)
#define minn(x1,x2,x3) min(min(x1,x2),x3)
const int INF=0x3f3f3f3f;
const int maxn = 305;
struct Edge
{
    int to,next;
    int val;
}edge[maxn<<1];
int head[maxn];
int maxedge;
inline void addedge(int u,int v,int c)
{
    edge[++maxedge] = (Edge) { v,head[u],c };
    head[u] = maxedge;
    edge[++maxedge] = (Edge) { u,head[v],c };
    head[v] = maxedge;
}
int n,s;
void init()
{
    scanf("%d%d",&n,&s);
    memset(head,-1,sizeof(head));
    maxedge=-1;
    for(int i=1;i<n;i++)
    {
        int u,v,c;
        scanf("%d%d%d",&u,&v,&c);
        addedge(u,v,c);
    }
}
void Find(int,int);
int dis[maxn];
bool chain[maxn]; // to mark if is on the chain
int end1,end2;
void dfs1(int u,int father,int deep,int &end)
{
    dis[u] = deep;
    if(!end || dis[end]<dis[u]) end=u;
    for(int i=head[u];~i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==father) continue;
        dfs1(v,u,deep+edge[i].val,end);
    }
}
bool dfs2(int u,int father,int end,int half)
{
    for(int i=head[u];~i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==father) continue;
        if(v==end || dfs2(v,u,end,half))
        {
            if(half>=dis[u] && half<dis[v]) Find(u,v);
            return true;
        }
    }
    return false;
}
void dfs3(int u,int father)
{
    for(int i=head[u];~i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==father) continue;
        dfs3(v,u);
        smax(dis[u],dis[v]+edge[i].val);
    }
}
bool dfs4(int u,int father,int end)
{
    for(int i=head[u];~i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(v==father) continue;
        if(v==end || dfs4(v,u,end))
        {
            chain[u]=true;
            return true;
        }
    }
    return false;
}
int S,T;
struct Info
{
    int u,from,val,id;
    bool operator < (const Info t) const { return val < t.val; }
};
priority_queue <Info> que;
void Find(int u,int v)
{
    int ans=INF,tmp=0;
    memset(dis,0,sizeof(dis));
    dfs3(v,-1); // only node as v
    for(int i=head[v];~i;i=edge[i].next) smax(tmp,dis[edge[i].to]+edge[i].val);
    smin(ans,tmp);
    memset(dis,0,sizeof(dis));
    dfs3(u,-1); S=T=u; tmp=0;
    for(int i=head[u];~i;i=edge[i].next)
        if(chain[edge[i].to]) que.push((Info){edge[i].to,u,dis[edge[i].to]+edge[i].val,i});
        else smax(tmp,dis[edge[i].to]+edge[i].val);
    int totlens=0;
    while(!que.empty())
    {
        Info now = que.top(); que.pop();
        if(now.from^S && now.from^T)
        {
            smax(tmp,now.val);
            continue;
        }
        if(totlens+edge[now.id].val>s)
        {
            smin(ans,max(now.val,tmp));
            printf("%d",ans);
            exit(0);
        }
        for(int i=head[now.u];~i;i=edge[i].next)
        {
            int v=edge[i].to;
            if(v==now.from) continue;
            if(chain[v]) que.push((Info){v,now.u,dis[v]+edge[i].val,i}); // dis is settled to be the left max_dis
            else smax(tmp,dis[v]+edge[i].val);
        }
        if(now.from==S) S=now.u;
        else T=now.u;
        totlens+=edge[now.id].val;
    }
}
void work()
{
    end1=end2=0;
    dfs1(1,-1,0,end1);
    dfs1(end1,-1,0,end2);
    dfs4(end1,-1,end2); chain[end1]=true; chain[end2]=true;
    int half=dis[end2]>>1;
    dfs2(end1,-1,end2,half);
}
int main()
{
    freopen("core.in","r",stdin);
    freopen("core.out","w",stdout);
    init();
    work();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值