FarmCraft——树形DP+贪心

题目描述

  • In a village called Byteville, there are houses connected with N-1 roads. For each pair of houses, there is a unique way to get from one to another. The houses are numbered from 1 to . The house no. 1 belongs to the village administrator Byteasar. As part of enabling modern technologies for rural areas framework, computers have been delivered to Byteasar's house. Every house is to be supplied with a computer, and it is Byteasar's task to distribute them. The citizens of Byteville have already agreed to play the most recent version of FarmCraft (the game) as soon as they have their computers. Byteasar has loaded all the computers on his pickup truck and is about to set out to deliver the goods. He has just the right amount of gasoline to drive each road twice. In each house, Byteasar leaves one computer, and immediately continues on his route. In each house, as soon as house dwellers get their computer, they turn it on and install FarmCraft. The time it takes to install and set up the game very much depends on one's tech savviness, which is fortunately known for each household. After he delivers all the computers, Byteasar will come back to his house and install the game on his computer. The travel time along each road linking two houses is exactly 1 minute, and (due to citizens' eagerness to play) the time to unload a computer is negligible. Help Byteasar in determining a delivery order that allows all Byteville's citizens (including Byteasar) to start playing together as soon as possible. In other words, find an order that minimizes the time when everyone has FarmCraft installed.

输入格式

  • The first line of the standard input contains a single integer (N(2<=N<=500000)) that gives the number of houses in Byteville.

  • The second line contains N integers (C_1,C_2,C_3,...C_n(1<=C_i<=10^9)), separated by single spaces; is the installation time (in minutes) for the dwellers of house no. i.

  • The next N-1 lines specify the roads linking the houses. Each such line contains two positive integers (a) and (b(1<=a < b<=N))separated by a single space. These indicate that there is a direct road between the houses no. a and b

输出格式

  • The first and only line of the standard output should contain a single integer: the (minimum) number of minutes after which all citizens will be able to play FarmCraft together

样例输入

6
1 8 9 6 3 2
1 3
2 3
3 4
4 5
4 6

样例输出

11

题目翻译

  • 在一个叫做比特村的小村庄中,有(n−1)条路连接着这个村庄中的全部(n)个房子。
    每两个房子之间都有一条唯一的通路。这些房子的编号为(1)(n)
    (1)号房子属于村庄的管理员比特安萨尔。
    为了提升村庄的科技使用水平,(n)台电脑被快递到了比特安萨尔的房子。每个房子都应该有一台电脑,且分发电脑的任务就落在了比特安萨尔的肩上。
    比特村的居民一致同意去玩农场物语这个游戏的最新快照版,而且好消息是他们很快就要得到他们最新的高配置电脑了。
    比特安萨尔将所有电脑都装在了他的卡车上,而且他准备好完成这个艰巨的任务了。
    他的汽油恰好够走每条路两遍。
    在每个房子边,比特安萨尔把电脑贴心的配送给居民,且立即前往下一个房子。(配送过程不花费任何时间)
    只要每间房子的居民拿到了他们的新电脑,它们就会立即开始安装农场物语。安装农场物语所用的时间根据居民的科技素养而定。幸运的是,每间房子中居民的科技素养都是已知的。
    在比特安萨尔配送完所有电脑后,他会回到他自己的(1)号房子去安装他自己的农场物语。
    用卡车开过每条路的时间恰好是(1)分钟,而居民开电脑箱的时间可以忽略不计。(因为他们太想玩农场物语了)
    请你帮助比特安萨尔算出从开始配送到所有居民都玩上了农场物语的最少时间。

解题思路

  • 这道题可以想到是树状DP,但是具体怎么写不是很好想
    每一个节点都是要遍历的,现在就是要求按什么顺序遍历最后所需的时间最少
    要注意在进行各个子节点的遍历时,时间不是相加的的,可以同步进行,也就是说在进行安装的时候可以走到别的地方进行安装
    这样,在选择先进入哪个子节点的时候就用到了贪心的思想,
    总时间当然就是安装的时间加上走路的时间
    走路的时间肯定是越来越大,要使总时间最小,如果先从等待时间短的开始,总时间就是最大安装时间再加上最大的走路时间,这个很显然,以为两个加数都是单增的
    所以,先走等待时间最长的点才能使决策最优
    那就把每个儿子记录下来,按等待时间从长到短排序,再进行运算
    详细内容看代码注释

代码

#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 5e5+5;
struct side {
    int t, next;
}e[N*2];
int head[N], tot;
void add(int x, int y) {
    e[++tot].next = head[x];
    head[x] = tot;
    e[tot].t = y;
}
int n, t[N], f[N], g[N], s[N], cnt;
//g[i]表示遍历i为根的子树的最短时间
//f[i]表示遍历完以i为跟的子树,且每人装好游戏的最短时间
bool judge(int x, int y) {
    return f[x] - g[x] > f[y] - g[y];
    //f[i]−g[i]表示等待的时间
}
void dfs(int x, int fx) {//树状DP
    if (x != 1) f[x] = t[x];//t[i]是读入的每个人安装游戏所需时间
    //1号点在第一次进入的时候是不安装游戏的,其余的点都是一进入就安装
    for(int i = head[x]; i; i = e[i].next)
        if (e[i].t != fx) dfs(e[i].t, x);
    //先遍历一边子节点
    cnt = 0;
    for(int i = head[x]; i; i = e[i].next)
        if (e[i].t != fx) s[++cnt] = e[i].t;
    //s数组记录x的儿子
    sort(s+1, s+cnt+1, judge);
    //将儿子按等待时间从大到小排序
    for(int i = 1; i <= cnt; i++) {
        f[x] = max(f[x], f[s[i]] + g[x] + 1);
        /*i=1时g[x]=0,f[s[i]]是安装完s[i]的子树的时间,
        1是过去所用的时间,g[x]是走过前面子节点的时间,
        由于回去的1包含在了安装时间里(安装时间>=1)所以这里不是加2*/
        g[x] += g[s[i]] + 2;
        //一来一回走两遍,+2
    }
}
int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; i++)
        scanf("%d", &t[i]);
    for(int i = 1; i < n; i++) {
        int x, y;
        scanf("%d%d", &x, &y);
        add(x, y), add(y, x);
        //存边
    }
    dfs(1, -1);
    printf("%d
", max(f[1], g[1] + t[1]));
    //f[1]是完除自己外所有人安装完游戏并回到1的时间
    //g[1]+t[1]是走遍全村在安装完自己的游戏的时间
    return 0;
}

补充

  • 有的人可能会这样写 就是我,但这样在递归的时候s存的就是子树所有的节点
    for(int i = head[x]; i; i = e[i].next)
        if (e[i].t != fx) dfs(e[i].t, x), s[++cnt] = e[i].t;
    
  • 还有人想到在函数里定义局部变量,但这样递归下去会十分费内存,会MLE的
原文地址:https://www.cnblogs.com/shawk/p/13204300.html