1. 程式人生 > 實用技巧 > FarmCraft

FarmCraft

題目描述

  • 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\n", 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的