计算客444 (2015计蒜之道 京东的物流路径)

针对京东物流网络,本文介绍了一种通过树分治算法来寻找树形物流网络中所有路径的最大总权值的方法。该算法首先找到树的重心,然后递归地处理各个子树,最终求得最大总权值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接

作为一个电子商务为主体的公司,京东一直努力实现自己“多、快、好、省”的承诺。其中,“快”的特质更是被京东发挥到了极致。京东建立了一个非常高效的物流网络,物流网络构成了一个树结构,由很多的物流点和将物流点连结起来的道路组成。

京东物流网络中每个物流点有一个权值 di,物流点间的道路也都有一个权值 wi。对于一条物流网络中的路径,令路径上所有物流点权值 di 的最小值为 mind,路径上所有道路权值 wi 的总和为 sumw,则该条路径的总权值为 mind* sumw。路径的起点和终点可以是物流网络中的任意物流点,且路径中不能出现重复的物流点。

请求出京东的这个树形物流网络所有路径总权值中的最大值。

输入格式

第一行输入一个整数 T(1 ≤ T ≤ 50),表示数据组数。

每组数据第一行一个整数 n(1 ≤ n ≤ 105),表示有 n 个物流点。

之后一行 n 个整数,表示每个物流点的权重 di(1 ≤ di ≤ 109)。

接下来有 n - 1 行,每行 3 个整数 ui,vi,wi(1 ≤ ui, vi ≤ n, 1 ≤ wi ≤ 109),表示有一条连接 ui 和 vi 的权值为 wi的道路。输入数据保证没有重复出现的(ui,vi)点对,且最终一定会形成一个树形物流网络。

最多有 10 组数据的 n 超过 104

输出格式

一共输出 T 行,每行一个整数,表示路径总权值的最大值。

样例1

输入:

1
3
1 2 3
1 2 1
1 3 2

输出:

3
提示信息

总权值最大的路径是 2 - 1 - 3,mind 为 min(1, 2, 3) = 1,sumw 为 sum(1, 2) = 3,因此总权值为 1 * 3 = 3。

思路:树分治。求得重心,以重心为树根遍历所在子树,求得所有点到根的路径信息(子树编号、路径长度、路径最小点权)。

关键在于如何高效率的更新答案,思路为先按照路径最小点权由小到大排序,从后往前遍历,对于当前路径,满足要求的解为,树编号不一样的且不小于其最小点权的路径长度最大的。所以要记录并更新之前的最长路径,和与最长路径子树编号不一样的次长路径,这样最优答案即为其中与当前路径编号不一样的一个。

当时比赛时不知道树分治的思想,普通思路一直超时。题解说点分治,并不知道什么意思,这几天学习了树分治想起这个题,便根据自己的理解敲了一遍。

#pragma comment(linker, "/STACK:10240000000,10240000000")
#include<iostream>
#include<stdio.h>
#include<math.h>
#include <string>
#include<string.h>
#include<map>
#include<queue>
#include<set>
#include<utility>
#include<vector>
#include<algorithm>
#include<stdlib.h>
using namespace std;
#define eps 1e-8
#define pii pair<int,int>
#define inf 0x3f3f3f3f
#define rd(x) scanf("%d",&x)
#define rd2(x,y) scanf("%d%d",&x,&y)
#define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define mo(x) memset(x,0,sizeof(x))
#define ll   long long int
#define mod 1000000007
#define maxn 101000
#define maxm 10000001
int mi(int a,int b){return a<b?a:b;}
int ma(int a,int b){return a>b?a:b;}
struct node
{
    int v,w,next;
}edge[maxn*2];
int head[maxn],vis[maxn],tot;
int d[maxn],sz[maxn],mx[maxn];
//ll dis[maxn];
ll res;
int n,nn;
void init(){
    tot=0;
    memset(head,-1,sizeof head);
    mo(vis);
}
void addedge(int u,int v,int w){
    edge[tot].v=v;edge[tot].w=w;edge[tot].next=head[u];
    head[u]=tot++;
}
void dfsize(int x,int fa){//统计子树大小
    sz[x]=1;mx[x]=0;
    for(int i=head[x];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        if(v==fa||vis[v]) continue;
        dfsize(v,x);
        sz[x]+=sz[v];
        if(sz[v]>mx[x]) mx[x]=sz[v];
    }
}
void dfsroot(int r,int x,int fa,int &root,int &mm){//求重心
    if(sz[r]-sz[x]>mx[x]) mx[x]=sz[r]-sz[x];
    if(mx[x]<mm) mm=mx[x],root=x;
    for(int i=head[x];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        if(v==fa||vis[v]) continue;
        dfsroot(r,v,x,root,mm);
    }
}
struct dd//保存到中心的路径的sumw、k(分支编号)、md(最小点权)
{
    ll w;
    int md,k;
    dd(){}
    dd(int kk,int mmd,ll ww){w=ww;md=mmd;k=kk;}
}dis[maxn];
bool cmp(dd a,dd b){
    return a.md<b.md;
}
void dfsdis(int r,int x,int fa,int k,ll ww,int md){//求得从中心出发的所有路径
    dis[nn++]=dd(k,md,ww);
    for(int i=head[x];i!=-1;i=edge[i].next){
        int v=edge[i].v;
        int w=edge[i].w;
        if(v==fa||vis[v]) continue;
        dfsdis(r,v,x,k,ww+w,mi(md,d[v]));
    }
}

void cal(int r){
    nn=0;
    int k=1;
    for(int i=head[r];i!=-1;i=edge[i].next)
    {
        int v=edge[i].v;
        int w=edge[i].w;
        if(vis[v]) continue;
        //dis[nn]=0;
        dfsdis(r,v,r,k,w,mi(d[v],d[r]));
        k++;
    }
    sort(dis,dis+nn,cmp);
    dd m1=dd(0,0,0);
    dd m2=dd(0,0,0);
    for(int i=nn-1;i>=0;i--){//遍历路径更新答案
        ll ww=m1.k==dis[i].k?m2.w:m1.w;
        ll res1=(ww+dis[i].w)*dis[i].md;
        res=res<res1?res1:res;
        if(dis[i].w>=m1.w){
            if(dis[i].k==m1.k) m1=dis[i];
            else {
                m2=m1;m1=dis[i];
            }
        }
        else if(dis[i].w>m2.w&&dis[i].k!=m1.k) m2=dis[i];
    }
}
void solve(int x){
    dfsize(x,x);
    int mm=n+1;
    int root;
    dfsroot(x,x,x,root,mm);//求重心
    cal(root);//计算过重心的最优答案
    vis[root]=1;
    for(int i=head[x];i!=-1;i=edge[i].next){//分治
        int v=edge[i].v;
        if(vis[v]) continue;
        solve(v);
    }
}
int main()
{
    int t,u,v,w;
    rd(t);
    while(t--){
        rd(n);
        init();
        for(int i=1;i<=n;i++) rd(d[i]);
        for(int i=1;i<n;i++){
            rd3(u,v,w);
            addedge(u,v,w);
            addedge(v,u,w);
        }
        res=0;
        solve(1);
        printf("%lld\n",res);
    }
    return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值