zoukankan      html  css  js  c++  java
  • 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;
    }
  • 相关阅读:
    linux_进程管理
    Linux-日志管理
    Httpd
    Linux-源码安装包管理
    Linux-计划任务管理
    Linux-LVM管理
    Linux-系统磁盘管理
    Linux-yum工具的使用
    Linux-rpm包管理
    Linux-网络进阶管理
  • 原文地址:https://www.cnblogs.com/andyqsmart/p/4795480.html
Copyright © 2011-2022 走看看