[日常训练] 旅行
来源:互联网 发布:js offsetparent属性 编辑:程序博客网 时间:2024/05/17 09:45
【问题描述】
小C上周末和他可爱的同学小A一起去X湖玩。
X湖景区一共有n个景点,这些景点由n-1条观光道连接着,从每个景点开始都可以通过观光道直接或间接地走到其他所有的景点。小C带着小A从1号景点开始游玩。游览完第一个景点后,先由小C决定下一个游览的景点,他们一起走去那个景点玩。接下来,他们轮流决定他们下一步去哪个景点玩。他们不会选择已经走过的景点,因为重复游览一个景点是无趣的。当他们无法选择下一个景点时,他们就结束旅程。
小C是好动的男孩纸,所以他希望游览的过程尽量长,也就是走过观光道的长度和最大。而小A是文静的女孩纸,她希望游览的过程尽量短。小A和小C都极度聪明,且他们的目光都足够长远,他们做出的决策都是对自己最优的。由于小C在旅游前就仔细研究了X湖景区的地图,他可以在旅行开始前就用自己惊人的数学能力推算出他和小A旅行的路径长度。
小C的梦境是美好的。在他的梦里,他和小A又进行了n-1次旅行,第i次旅行从i+1号点开始,每次也是小C先决定下一个景点,然后小A,然后小C……直到旅行结束。现在小C希望你对于所有n次旅行,求出他和小A旅行的路径长度。
【输入格式】
第一行一个正整数n,表示景点的个数。
接下来n-1行,每行三个正整数u,v,c。表示有一条连接u和v的双向观光道,其长度为c。
【输出格式】
输出一共N行,每行一个正整数。第i行表示从i号点开始旅行他们走过的路径长度。
【输入输出样例】
travel_sample1.in
5
1 2 1
1 3 2
2 4 3
2 5 4
travel_sample1.out
4
4
7
6
7
【样例解释】
从1号景点开始:
若小C选择走到3号景点,则小A无法选择下一个景点,旅行的路径长度为2
若小C选择走到2号景点,则小A会在4号景点和5号景点中选择更近的4号点,然后小C无法选择下一个景点,旅行结束,旅行的路径长度会是4
所以小C会选择走到2号点,最终的路径长度是4
【数据范围】
对于20%的数据,N ≤ 15
对于60%的数据,N ≤ 3000
对于100%的数据,N ≤ 300000, c[i] ≤ 1e9
【60分】O(n2) 树形DP
- 显然X湖景区是一个树形结构。原问题“不能重复”的限制等价于每次只能选择当前节点的子节点。
- 我们设
f[x] 表示小C从x 点开始往下走能走出的最大的路径长度,g[x] 表示小A从x 点开始往下走能走出的最小的路径长度。则转移为:(f[x]=Max(g[y]+lenx→y),g[x]=Min(f[y]+lenx→y) y 的x 的子节点,lenx→y 表示边x→y 的路径长度)。 - 然后我们以每个点为根,都做一遍这样的树形
DP ,复杂度为O(n2)
【100分】O(n) 换根 + 树形DP
- 可以发现:做很多遍树形
DP 是没有必要的,我们考虑每次O(1) 将树根转移到一个相邻的节点上 - 设旧树根为
x ,新树根为y ,则当我们将树根从x 转移到y 上时:其实f 和g 数组中只需要修改x 和y 的值。 - 那么如何修改呢?实际上我们并不需要真的去修改
f,g 数组,我们另外记h0[y],h1[y] ,分别表示从点y 开始往上走能走出的最小和最大的路径长度,那么此时若以y 为根,新的f′[y]=Max(h1[y],f[y]),g′[y]=Min(h0[y],g[y]) - 考虑
h0[y],h1[y] 如何转移,这里以h0[y] 为例,可以分两种情况讨论:(以下x 为y 的父节点)
[1]、若f[x] 是由f[y] 转移过来,h0[y] 只能由h1[x] 或f[x] 的次大值(记为fs[x] )转移过来,也就是[2]、若h0[y]=Max(h1[x],fs[x])+lenx→y(f[y]+lenx→y=f[x]) f[x] 不是由f[y] 转移过来,h0[y] 能由h1[x] 或f[x] 转移过来,也就是h0[y]=Max(h1[x],f[x])+lenx→y(f[y]+lenx→y≠f[x]) - 因此我们在最开始做的那遍树形
DP 中也要记录f,g 的次优值fs,gs ,之后再对树遍历一遍计算h0,h1 即可,总复杂度为O(n)
【代码】
//f[x][0]表示次优值,f[x][1]表示最优值,g数组同理 #include <iostream>#include <cstdio>using namespace std;typedef long long ll;const ll Maxn = 1000000000000000000ll;const int N = 3e5 + 5;ll f[N][2], g[N][2], h[N][2]; int n; struct Edge{ int to, cst; Edge *nxt;}p[N << 1], *T = p, *lst[N];inline ll Max(const ll &x, const ll &y) {return x > y ? x : y;}inline ll Min(const ll &x, const ll &y) {return x < y ? x : y;}inline void addEdge(const int &x, const int &y, const int &z){ (++T)->nxt = lst[x]; lst[x] = T; T->to = y; T->cst = z; (++T)->nxt = lst[y]; lst[y] = T; T->to = x; T->cst = z;}inline void Dfs1(const int &x, const int &fa){ g[x][0] = g[x][1] = Maxn; for (Edge *e = lst[x]; e; e = e->nxt) { int y = e->to, z = e->cst; if (y == fa) continue; Dfs1(y, x); if (g[y][1] + z > f[x][1]) f[x][0] = f[x][1], f[x][1] = g[y][1] + z; else if (g[y][1] + z > f[x][0]) f[x][0] = g[y][1] + z; if (f[y][1] + z < g[x][1]) g[x][0] = g[x][1], g[x][1] = f[y][1] + z; else if (f[y][1] + z < g[x][0]) g[x][0] = f[y][1] + z; } if (g[x][1] == Maxn) g[x][1] = 0; }inline void Dfs2(const int &x, const int &fa){ for (Edge *e = lst[x]; e; e = e->nxt) { int y = e->to, z = e->cst; if (y == fa) continue; h[y][0] = Max(h[x][1], (g[y][1] + z == f[x][1] ? f[x][0] : f[x][1])) + z; h[y][1] = Min(h[x][0], (f[y][1] + z == g[x][1] ? g[x][0] : g[x][1])) + z; Dfs2(y, x); }}inline int get(){ char ch; int res = 0; while ((ch = getchar()) < '0' || ch > '9'); res = ch - '0'; while ((ch = getchar()) >= '0' && ch <= '9') res = (res << 3) + (res << 1) + ch - '0'; return res;}inline void put(ll x){ if (x > 9) put(x / 10); putchar(x % 10 + 48);}int main(){ freopen("travel.in", "r", stdin); freopen("travel.out", "w", stdout); n = get(); int u, v, x; for (int i = 1; i < n; ++i) { u = get(); v = get(); addEdge(u, v, get()); } Dfs1(1, 0); for (Edge *e = lst[x = 1]; e; e = e->nxt) { int y = e->to, z = e->cst; h[y][0] = (g[y][1] + z == f[x][1] ? f[x][0] : f[x][1]) + z; h[y][1] = (f[y][1] + z == g[x][1] ? g[x][0] : g[x][1]) + z; //这里注意要特别处理根的子节点情况 Dfs2(y, x); } for (int i = 1; i <= n; ++i) put(Max(f[i][1], h[i][1])), putchar('\n');}
- [日常训练] 旅行
- HEU日常训练10.02
- 日常训练小结
- 日常训练20161012 道路网
- 日常训练20161012 醉酒
- 日常训练20161014 跟踪
- 日常训练20161018 证据
- 日常训练20161018 subset
- 日常训练 平均数
- 日常训练 水箱
- 日常训练 棋盘游走
- 日常训练 20170531 数字
- 日常训练 20170531 探险
- 日常训练 20170531 矩阵
- 日常训练 20170602 Book
- 日常训练 20170602 Equation
- 日常训练 20170603 棋盘
- 日常训练 20170605 EasyProblem
- 2日志72/300
- Word Folding
- css案例(二)
- Pandas 中map, applymap and apply的区别
- Android6.0 运行权限的理解以及封装于基类的用法
- [日常训练] 旅行
- 文件拷贝并监听
- ?最大子列和问题(Java)/[3]53. Maximum Subarray(Java)
- 【剑指Offer】面试题44:扑克牌的顺序
- hihocoder #1539 : 数组重排3
- vue.js学习小结
- 文章标题
- DenseNet的使用
- 五、java中的关键字