FZU - 2256 迷宫(树上深搜最小花费)

某一天,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("\n");
    }
}
posted @ 2018-06-10 18:43  KuroNekonano  阅读(145)  评论(0编辑  收藏  举报