zoukankan      html  css  js  c++  java
  • FZU

    某一天,YellowStar在人生的道路上迷失了方向,迷迷糊糊之中,它误入了一座迷宫中,幸运的是它在路口处发现了一张迷宫的地图。

    经过它的观察,它发现这个迷宫一共有n个房间,并且这n个房间呈现一个有根树结构,它现在所在的1号房间为根,其它每个房间都有一个上级房间,连接第i个房间和它的上级房间Pi的道路长度为Wi。

    在地图的背面,记载了这个迷宫中,每个房间拥有一个时空传送门,第i个房间的传送门可以花费Di单位的时间传送到它的任意一个下级房间中(如果x是y的下级房间,并且y是z的下级房间,那么x也是z的下级房间)。

    YellowStar的步行速度为1单位时间走1长度,它现在想知道从1号房间出发,到每一个房间的最少时间。

    Input
    包含多组测试数据。

    第一行输入n表示n个房间。

    第二行输出n个数字,第i个数字Di表示i号房间传送器需要花费的时间。

    接下来n-1行,第i行包含两个数字Pi和Wi,表示i+1号房间的上级房间为Pi,道路长度为Wi。

    1≤n≤100000

    1≤Di, Wi≤10^9

    Output
    输出n个数,第i个数表示从1号房间出发到i号房间的最少时间。 (注意,输出最后一个数字后面也要加一个空格)

    Sample Input
    5
    99 97 50 123 550
    1 999
    1 10
    3 100
    3 44
    Sample Output
    0 99 10 60 54
    Hint
    初始在1号房间,到1号房间的代价为0。

    通过1号房间的传送门传送到2号房间,到2号房间的代价为99。

    通过1号房间走到3号房间,到3号房间的代价为10。

    通过1号房间走到3号房间,在通过3号房间的传送门传送到4号房间,到4号房间的代价为60。

    通过1号房间走到3号房间,在通过3号房间走到5号房间,到5号房间的代价为54。

    分析:在一个树的根节点,求到达树上每个节点的最小花费。对于一个传送门可以到达其节点子树上的任意一个几点。首先可以明确,我们从根部走到某个节点的过程,要么是全程走过去的。要么是走一段,然后通过传送门直接到达,或者根本不走,直接通过传送门到达。

    根本不存在先传送门,然后走一会儿到达的情况,也就是说不可能有两种方式交替的情况行走。因为如果我能通过传送门到达的节点,为什么还要中途停下来去走路?明明可以直达的位置,还要转换方式再去徒增一段行走的花费。

    这样就转换成了一个问题。对于一个节点,要么从根节点直接走过来,这种情况直接求和即可得到。要么从到达该节点路径上的某一个父节点直达。也就是用路径上所有父节点的传送门花费,松弛走到该点的最小花费。

    对于当前结点,找其路径上的父亲松弛花费,对于子节点,深搜下去,用已经达到最优了的当前结点直接通过走的方式到达下一个结点。而下一个结点可能也可能通过路径上的某一个父节点直接传送过来。

    即使是这样对于题目中提到的1e5个节点的数据量,仍然可能会出现单链树的情况,这样一来就成了O(n^2)的算法,并且,因为路径上的传送门花费是小于1e9的,因此到达任意一个节点,最大花费都不会超过1e9,因为一旦通过走的花费之后超过了1e9,其路径上必有某个节点的传送花费小于行走花费。因此都可以通过最大1e9的传送到达。

    #include<stdio.h>
    #include<string.h>
    #include<vector>
    #include<algorithm>
    #define LL long long
    using namespace std;
    const int maxn=1e5+10;
    struct edge
    {
        int val,to;
        edge() {}
        edge(int a,int b)
        {
            val=a;
            to=b;
        }
    };
    int n,path[maxn],num;
    int di[maxn],dist[maxn];
    vector<edge>mp[maxn];
    void dfs(int now)
    {
        path[num++]=now;
        for(int i=0; i<num; i++)
            dist[now]=min(dist[now],dist[path[i]]+di[path[i]]);
        for(int i=0; i<mp[now].size(); i++)
        {
            edge to=mp[now][i];
            dist[to.to]=dist[now]+to.val;
            dfs(to.to);
        }
        num--;
    }
    int main()
    {
        while(scanf("%d",&n)!=EOF)
        {
            int val,fa;
            for(int i=0; i<=n; i++)mp[i].clear();
            for(int i=1; i<=n; i++)scanf("%d",&di[i]);
            for(int i=2; i<=n; i++)
            {
                scanf("%d%d",&fa,&val);
                mp[fa].push_back(edge(val,i));
            }
            dist[1]=0;
            num=0;
            dfs(1);
            for(int i=1; i<=n; i++) printf("%d ",dist[i]);
            printf("
    ");
        }
    }
    
  • 相关阅读:
    Win7双击任务栏图标导致窗口还原的问题
    一致性哈希算法及其在分布式系统中的应用(转)
    CAP理论(转)
    从Android界面开发谈起(转)
    Android开发入门之Window 环境概念介绍(转)
    数据库缓存技术(转)
    VoltDB开篇 简介(转)
    window下如何让php支持openssl(转)
    mysql分表的3种方法(转)
    linux crontab 每10秒执行一次
  • 原文地址:https://www.cnblogs.com/kuronekonano/p/11135750.html
Copyright © 2011-2022 走看看