旅行

题目描述
小C上周末和他可爱的同学小A一起去X湖玩。
X湖景区一共有n个景点,这些景点由n-1条观光道连接着,从每个景点开始都可以通过观光道直接或间接地走到其他所有的景点。小C带着小A从1号景点开始游玩。游览完第一个景点后,先由小C决定下一个游览的景点,他们一起走去那个景点玩。接下来,他们轮流决定他们下一步去哪个景点玩。他们不会选择已经走过的景点,因为重复游览一个景点是无趣的。当他们无法选择下一个景点时,他们就结束旅程。
小C是好动的男孩纸,所以他希望游览的过程尽量长,也就是走过观光道的长度和最大。而小A是文静的女孩纸,她希望游览的过程尽量短。小A和小C都极度聪明,且他们的目光都足够长远,他们做出的决策都是对自己最优的。由于小C在旅游前就仔细研究了X湖景区的地图,他可以在旅行开始前就用自己惊人的数学能力推算出他和小A旅行的路径长度。
小C的梦境是美好的。在他的梦里,他和小A又进行了n-1次旅行,第i次旅行从i+1号点开始,每次也是小C先决定下一个景点,然后小A,然后小C……直到旅行结束。现在小C希望你对于所有n次旅行,求出他和小A旅行的路径长度。
输入
第一行一个正整数n,表示景点的个数。
接下来n-1行,每行三个正整数u,v,c。表示有一条连接u和v的双向观光道,其长度为c。
输出
输出一共N行,每行一个正整数。第i行表示从i号点开始旅行他们走过的路径长度。

样例输入
5
1 2 1
1 3 2
2 4 3
2 5 4
样例输出
4
4
7
6
7

提示
从1号景点开始:
若小C选择走到3号景点,则小A无法选择下一个景点,旅行的路径长度为2
若小C选择走到2号景点,则小A会在4号景点和5号景点中选择更近的4号点,然后小C无法选择下一个景点,旅行结束,旅行的路径长度会是4
所以小C会选择走到2号点,最终的路径长度是4
【数据范围】
对于20%的数据,N ≤ 15
对于60%的数据,N ≤ 3000
对于100%的数据,N ≤ 300000, c[i] ≤ 1e9

Solution

n^2的数据可以每次枚举树根,然后用一个简单的dp实现

void dp(int k,int pre)
{
    int son=0; 
    for(int i=head[k];i!=-1;i=Next[i]) 
    if(to[i]!=pre) 
    {
        son=1; 
        dp(to[i],k);
        f[k]=max(f[k],g[to[i]]+len[i]);
        g[k]=min(g[k],f[to[i]]+len[i]);
    }
    if(!son) g[k]=0; 
}   

我们发现,如果以1为根形成的一棵树,做了一遍dp后,为了求每个点作为树根的f[i],必须把i上面的子树的情况也要考虑进去。
sf[i]表示i上面的子树的f值
sg[i]同理
求起来有些繁琐,min和max之类的和初始化一定要搞清楚。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
int n,x,y,z,tot;
int head[300005],Next[600005],len[600005],to[600005];
ll f[300005],g[300005],sf[300005],sg[300005],ans[300005];
void dp(int k,int pre)
{
    int son=0; 
    for(int i=head[k];i!=-1;i=Next[i]) 
    if(to[i]!=pre) 
    {
        son=1; 
        dp(to[i],k);
        f[k]=max(f[k],g[to[i]]+len[i]);
        g[k]=min(g[k],f[to[i]]+len[i]);
    }
    if(!son) g[k]=0; 
}       
void add(int x,int y,int z)
{
    Next[tot]=head[x];
    to[tot]=y;
    len[tot]=z;
    head[x]=tot;
    tot++;
}
void DP(int k,int pre)
{
    ll s1=1e16,s2=1e16; 
    int p1=0,p2=0; 
    for(int i=head[k];i!=-1;i=Next[i]) 
    if(to[i]!=pre) 
    {
        if(f[to[i]]+len[i]<s1) 
        {
            s2=s1;
            p2=p1;
            s1=f[to[i]]+len[i];
            p1=to[i];
        }
        else
        if(f[to[i]]+len[i]<s2) 
        {
            s2=f[to[i]]+len[i];
            p2=to[i];
        }
    }
    for(int i=head[k];i!=-1;i=Next[i]) //求sf
    if(to[i]!=pre) 
    {
        if(p1!=to[i]) sf[to[i]]=min(sf[to[i]],s1+len[i]); else sf[to[i]]=min(sf[to[i]],s2+len[i]);
        sf[to[i]]=min(sf[to[i]],sg[k]+len[i]); 
    }
    s1=s2=0;
    p1=p2=0;
    for(int i=head[k];i!=-1;i=Next[i]) 
    if(to[i]!=pre) 
    {
        if(g[to[i]]+len[i]>s1) 
        {
            s2=s1;
            p2=p1;
            s1=g[to[i]]+len[i];
            p1=to[i];
        }
        else
        if(g[to[i]]+len[i]>s2) 
        {
            s2=g[to[i]]+len[i];
            p2=to[i];
        }
    }
    for(int i=head[k];i!=-1;i=Next[i]) //求sg
    if(to[i]!=pre) 
    {
        if(p1!=to[i]) sg[to[i]]=max(sg[to[i]],s1+len[i]); else sg[to[i]]=max(sg[to[i]],s2+len[i]);
        sg[to[i]]=max(sg[to[i]],sf[k]+len[i]); 
    }
    for(int i=head[k];i!=-1;i=Next[i]) 
    if(to[i]!=pre) 
    {
        ans[to[i]]=max(f[to[i]],sf[to[i]]); 
        DP(to[i],k);
    }
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) head[i]=-1;
    for(int i=1;i<=n;i++) 
    {
        f[i]=sg[i]=0;
        g[i]=sf[i]=1e16;
    }
    for(int i=1;i<n;i++) 
    {
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);
        add(y,x,z);
    }
    dp(1,0);
    ans[1]=f[1]; sg[1]=1e16; sf[1]=0; 
    DP(1,0);
    for(int i=1;i<=n;i++) printf("%lld\n",ans[i]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值