Codeforces 633 F The Chocolate Spree(树形dp,两条不相交链节点权值和最大)
2016-08-03 13:49
477 查看
题目链接:
Codeforces 633 F The Chocolate Spree
题意:
给一个n个节点的树和n−1条边,每个点有一个权值,从树中选择两条不相交的链(无公共节点)使得两条链上节点权值和最大?
数据范围:n≤105,value[i]≤109
分析:
参考博客:逍遥丶綦
先dfs处理出每个节点向其子树最多可以得到的链的最大权值和down[v](必须由v节点向下)和在子树中最长链的权值best[v](可以由一棵子树从下往上经过v节点然后转向另一棵子树,也可以是某棵子树的内部的最长链)。
然后借助队列遍历每个u和其儿子v“截断”这条边(以这条边为分界线),求的两棵树的内部最长链。这时就相当于把v节点和u以及u的其余儿子节点都分开了。我们先求u及u除了v以外的儿子节点所形成的树的最长链,记为outside。
并记up[u]为u向上可获得的最长链(不包括u节点),predown[]和
ppredown[]为u的儿子的down[]数组的前缀最大和次大,prebest[]为儿子的前缀最大,sufdown[],ssufdown[],sufbest[]分别为相应的后缀down[]数组最大和次大,best[]数组最大。
考虑outside可以获得的组合,当前枚举到第i个儿子:
up[u]+value[u]+max(predown[i−1],sufdown[i+1])
value[u]+predown[i−1]+sufdown[i+1]
value[u]+predown[i−1]+ppredown[i−1]
value[u]+sufdown[i+1]+ssufdown[i+1]
max(prebest[i−1],sufbest[i+1])
更新:
ans=max(ans,outside+best[v]),第i个儿子是v
up[v]=value[u]+max(up[u],max(predown[i−1],sufdown[i+1]));up[v]不包括v节点权值
细节还是挺多的。而且借助队列和前后缀处理太强啦。
时间复杂度:O(n)
Codeforces 633 F The Chocolate Spree
题意:
给一个n个节点的树和n−1条边,每个点有一个权值,从树中选择两条不相交的链(无公共节点)使得两条链上节点权值和最大?
数据范围:n≤105,value[i]≤109
分析:
参考博客:逍遥丶綦
先dfs处理出每个节点向其子树最多可以得到的链的最大权值和down[v](必须由v节点向下)和在子树中最长链的权值best[v](可以由一棵子树从下往上经过v节点然后转向另一棵子树,也可以是某棵子树的内部的最长链)。
然后借助队列遍历每个u和其儿子v“截断”这条边(以这条边为分界线),求的两棵树的内部最长链。这时就相当于把v节点和u以及u的其余儿子节点都分开了。我们先求u及u除了v以外的儿子节点所形成的树的最长链,记为outside。
并记up[u]为u向上可获得的最长链(不包括u节点),predown[]和
ppredown[]为u的儿子的down[]数组的前缀最大和次大,prebest[]为儿子的前缀最大,sufdown[],ssufdown[],sufbest[]分别为相应的后缀down[]数组最大和次大,best[]数组最大。
考虑outside可以获得的组合,当前枚举到第i个儿子:
up[u]+value[u]+max(predown[i−1],sufdown[i+1])
value[u]+predown[i−1]+sufdown[i+1]
value[u]+predown[i−1]+ppredown[i−1]
value[u]+sufdown[i+1]+ssufdown[i+1]
max(prebest[i−1],sufbest[i+1])
更新:
ans=max(ans,outside+best[v]),第i个儿子是v
up[v]=value[u]+max(up[u],max(predown[i−1],sufdown[i+1]));up[v]不包括v节点权值
细节还是挺多的。而且借助队列和前后缀处理太强啦。
时间复杂度:O(n)
#include <stdio.h> #include <string.h> #include <algorithm> #include <math.h> #include <climits> #include <vector> #include <queue> using namespace std; typedef long long ll; const int MAX_N = 100010; int n, total; int head[MAX_N]; ll value[MAX_N], best[MAX_N], down[MAX_N]; ll prebest[MAX_N], sufbest[MAX_N], predown[MAX_N], ppredown[MAX_N]; ll sufdown[MAX_N], ssufdown[MAX_N], up[MAX_N]; struct Edge { int to, next; }edge[MAX_N * 2]; inline void AddEdge(int from, int to) { edge[total].to = to, edge[total].next = head[from]; head[from] = total++; } void dfs(int u, int p) { ll Max = 0, MMax = 0; for (int i = head[u]; i != -1; i = edge[i].next) { int v = edge[i].to; if (v == p) continue; dfs(v, u); if (down[v] > Max) { MMax = Max; Max = down[v]; } else if (down[v] > MMax) { MMax = down[v]; } best[u] = max(best[u], best[v]); } down[u] = Max + value[u]; best[u] = max(best[u], Max + MMax + value[u]); // best可以经过根节点 } void solve() { ll ans = 0; queue<pair<int, int> > que; que.push(make_pair(1, 0)); while (!que.empty()) { pair<int, int> cur = que.front(); int u = cur.first, p = cur.second; que.pop(); vector<int> child; child.push_back(0); for (int i = head[u]; i != -1; i = edge[i].next) { int v = edge[i].to; if (v != p) child.push_back(v); } int size = child.size(); // 记录down数组前缀最大和次大,best数组前缀最大 prebest[0] = predown[0] = ppredown[0] = 0; for (int i = 1; i < size; ++i) { int v = child[i]; prebest[i] = max(prebest[i - 1], best[v]); predown[i] = predown[i - 1], ppredown[i] = ppredown[i - 1]; if (down[v] > predown[i]) { ppredown[i] = predown[i]; predown[i] = down[v]; } else if (down[v] > ppredown[i]) { ppredown[i] = down[v]; } } // 记录down数组后缀最大和次大,best数组后缀最大 sufbest[size] = sufdown[size] = ssufdown[size] = 0; for (int i = size - 1; i >= 1; --i) { int v = child[i]; sufbest[i] = max(sufbest[i + 1], best[v]); sufdown[i] = sufdown[i + 1], ssufdown[i] = ssufdown[i + 1]; if (down[v] > sufdown[i]) { ssufdown[i] = sufdown[i]; sufdown[i] = down[v]; } else if (down[v] > ssufdown[i]) { ssufdown[i] = down[v]; } } // 遍历每个儿子,并且每个儿子的best是一条链 for (int i = 1; i < size; ++i) { int v = child[i]; ll outside = up[u] + value[u] + max(predown[i - 1], sufdown[i + 1]); outside = max(outside, value[u] + predown[i - 1] + ppredown[i - 1]); outside = max(outside, value[u] + sufdown[i + 1] + ssufdown[i + 1]); outside = max(outside, value[u] + predown[i - 1] + sufdown[i + 1]); outside = max(outside, max(prebest[i - 1], sufbest[i + 1])); ans = max(ans, outside + best[v]); } // 更新儿子的up并将儿子压进队列 for (int i = 1; i < size; ++i) { int v = child[i]; up[v] = value[u] + max(up[u], max(predown[i - 1], sufdown[i + 1])); que.push(make_pair(v, u)); } } printf("%lld\n", ans); } int main() { while (~scanf("%d", &n)) { for (int i = 1; i <= n; ++i) { scanf("%lld", &value[i]); } memset(head, -1, sizeof(head)); total = 0; for (int i = 1; i < n; ++i) { int u, v; scanf("%d%d", &u, &v); AddEdge(u, v); AddEdge(v, u); } memset(best, 0, sizeof(best)); memset(down, 0, sizeof(down)); memset(up, 0, sizeof(up)); dfs(1, 0); solve(); } return 0; }
相关文章推荐
- Codeforces 633 F The Chocolate Spree(树形dp,两条不相交链节点权值和最大)
- Codeforces 294E Shaass the Great 树形dp(水
- 【codeforces 274B】【树形DP】 B. Zero Tree【一棵树,每个点有权值,每次操作可以对一个联通子集中的点全部加或者减1,且每次操作必须包含点1,问最少多少次操作权值全为0】
- hdu4616 树形dp(有限制的权值最大链)
- Codeforces 682C Alyona and the Tree(树形DP)
- 【HDU1520】Anniversary Party-树形DP求树的最大权值独立集
- 树形dp ,求最大路径权值,最长路径
- SPOJ Two Paths(树形dp,最大不相交路径长度乘积)
- Codeforces 835 F Roads in the Kingdom(树形dp)
- HDU 4616 Game(经典树形dp+最大权值和链)
- poj 2486 树形dp(给定步数 走一棵树,获得的节点值总和的最大值)
- HDU 5242Game 树上的贪心 树形dp 求出使K条链的权值总和最大
- codeforces 294E Shaass the Great (树形dp,好题)
- CodeForces 294E Shaass the Great 树形DP
- ZOJ 4772 Treasure Hunt I 树形DP(背包) && hdu The Ghost Blows Light 树形DP(背包)
- HDOJ 4276 The Ghost Blows Light(树形DP)
- SPFA + 树形DP:The Ghost Blows Light
- codeforces 61D Eternal Victory 树形DP
- codeforces 120F Spiders 树形DP
- HDU 4276 The Ghost Blows Light 树形dp