ACM学习历程—FZU2195 检查站点(树形DP || 贪心)

Description

在山上一共有N个站点需要检查,检查员从山顶出发去各个站点进行检查,各个站点间有且仅有一条通路,检查员下山前往站点时比较轻松,而上山时却需要额外的时间,问最后检查员检查完所有站点时所需要的额外时间最少是多少。

 

Input

包含多组数据 每组数据输入第一行为一个整数N 表示站点个数(1<=N<=100000),接下去N-1 行 每行3个整数 x,y,z(1<=z<=10000) 检查站x为检查站y的父节点,x,y之间有一条通路,从y到x需要额外z的时间。(父节点在子节点上方,山顶固定标号为1)

Output

输出一行一个整数表示最少需要花费的额外时间。

Sample Input

6

1 2 1

2 4 1

1 3 1

3 5 1

3 6 1

Sample Output

3

 

题目乍一看像旅行商问题,但是n这么大,显然不是这么玩的。

考虑到树形结构的特殊性,就是从左子树到右子树,一定要进过父节点才能到达,这点是跟图不一样的。

于是从过程来看,我如果设p[i]表示遍历以i为根的子树所需的最小花费。all[i]表示遍历完并回到i结点的最小花费。当然,起始都是从i结点出发。

那么对于i来说,all[i] = sum(all[k]+dis[k][i])(k是i的子节点)

p[i] = min(sum(all[k]+dis[k][i])+p[j])(其中k != j)

这样的话,用记忆化搜索,便可以得到所有的p和all。p[1]即为所要求。

复杂度是O(n)

但是直接从结果上来看的话,整个遍历的路径,一定是每个线段都一上一下各一次,但是有一条从根部到底部的路径只下不上。(这个从之前的特殊性前提也可以推来)

这样便只需要找这样一条耗费最大的路径,然后用所有路径的和减去它即可。

复杂度也是O(n),但是可以省掉两个数组。

代码:(dp)

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <algorithm>
#define LL long long

using namespace std;

const int maxN = 1e5+5;
int n;
LL all[maxN], p[maxN];

inline LL myMin(LL x, LL y)
{
    if (x == -1)
        return y;
    else
        return x < y ? x : y;
}

//链式前向星
struct Edge
{
    int to, next;
    int val;
}edge[maxN];

int head[maxN], cnt;

void addEdge(int u, int v, int val)
{
    edge[cnt].to = v;
    edge[cnt].val = val;
    edge[cnt].next = head[u];
    head[u] = cnt;
    cnt++;
}

void initEdge()
{
    memset(head, -1, sizeof(head));
    cnt = 0;
}

void input()
{
    initEdge();
    memset(all, -1, sizeof(all));
    memset(p, -1, sizeof(p));
    int x, y, z;
    for (int i = 1; i < n; ++i)
    {
        scanf("%d%d%d", &x, &y, &z);
        addEdge(x, y, z);
    }
}

void dfs(int now)
{
    LL s = 0;
    int to;
    for (int k = head[now]; k != -1; k = edge[k].next)
    {
        to = edge[k].to;
        if (all[to] == -1)
            dfs(to);
        s += all[to]+edge[k].val;
    }
    all[now] = s;
    for (int k = head[now]; k != -1; k = edge[k].next)
    {
        to = edge[k].to;
        p[now] = myMin(p[now], s-all[to]-edge[k].val+p[to]);
    }
    if (p[now] == -1)
        p[now] = 0;
}

void work()
{
    dfs(1);
    printf("%I64d
", p[1]);
}

int main()
{
    //freopen("test.in", "r", stdin);
    while (scanf("%d", &n) != EOF)
    {
        input();
        work();
    }
    return 0;
}
原文地址:https://www.cnblogs.com/andyqsmart/p/4795480.html