洛谷P3574 [POI2014] FAR-FarmCraft(树形dp)

洛谷 P 3574 [ P O I 2014 ] F A R − F a r m C r a f t (树形 d p ) \Huge{洛谷P3574 [POI2014] FAR-FarmCraft(树形dp)} 洛谷P3574[POI2014]FAR−FarmCraft(树形dp)

文章目录

题目链接:P3574 [POI2014] FAR-FarmCraft - 洛谷

题意

给出 n n n个节点、 n − 1 n-1 n−1条边的一棵无根树;经过每条边所用时间都为 1 1 1。然后每个节点都有点权 b [ i ] b[i] b[i]。

题目故事:管理员从自己家(一号节点)出发,然后将一批电脑分发给每个节点的居民,每个居民得到电脑后会立即开始下载游戏,下载游戏的时间为点权 b [ i ] b[i] b[i]。

管理员每次经过某节点就相当于直接分发给居民。

管理员的车恰好能够走每条路两遍。

要求计算出管理员从开始配送到所有居民都能玩上游戏所用最少时间。

题目说明

给一棵树,走过每条边需要花费一个时间,安装软件又需要花费一个时间,需要遍历整棵树并回到起点,想让所有点中到达时间+安装时间的最大值最小,问这个值是多少?

思路

跟据题目要求,显然能够知道,题目要求求出所有居民中最后玩上游戏的居民所用时间,并且要求这个时间最小。

跟据题目说明 ,我们可以想到:对于同一个节点的子节点,大致的贪心思路是先遍历点权最大的节点

但是可能会出现一种情况(画的比较丑陋,多多见谅):

但是我们可以注意到题目有限制条件**"管理员的车恰好能够走每条路两遍"**,那么我们就不用考虑这种情况了。

一般地,我们用 f [ i ] f[i] f[i]表示节点 i i i这棵子树中的最大值 , d e p [ i ] dep[i] dep[i]表示从节点 i i i出发遍历其子树并返回所用的总时间 , b [ i ] b[i] b[i]表示点权

容易发现,这道题属于树形dp中的选择节点型,只不过需要判断的是选择子节点的先后顺序。

那么对应的状态转移方程即为:
f [ x ] = max ⁡ ( f [ x ] , d e p [ x ] + max ⁡ ( f [ i ] , f [ j ] + d e p [ i ] + 2 ) + 1 ) f[x]=\max(f[x], dep[x]+\max(f[i],f[j]+dep[i]+2)+1) f[x]=max(f[x],dep[x]+max(f[i],f[j]+dep[i]+2)+1)

其中 x x x为当前节点, i , j i,j i,j表示它的其中两个子节点。需要解释一下:

  • 方程中的+2是因为从一个子节点到另一个子节点的两段路。
  • 方程中的+1是因为从 x x x节点到子节点的一段路。

但是该这样进行状态转移,我们需要对子节点相互比较,其时间复杂度为 O ( n 2 ) O(n^2) O(n2),会超时。

我们考虑优化状态转移方程:

  • 如果先 i i i后 j j j: max ⁡ ( f [ i ] , f [ j ] + d e p [ i ] + 2 ) \max(f[i],f[j]+dep[i]+2) max(f[i],f[j]+dep[i]+2)

  • 如果先 j j j后 i i i: max ⁡ ( f [ j ] , f [ i ] + d e p [ j ] + 2 ) \max(f[j],f[i]+dep[j]+2) max(f[j],f[i]+dep[j]+2)

  • 因为 f [ i ] < f [ i ] + d e p [ j ] + 2 f[i] <f[i]+dep[j]+2 f[i]<f[i]+dep[j]+2且 f [ j ] < f [ j ] + d e p [ i ] + 2 f[j]<f[j]+dep[i]+2 f[j]<f[j]+dep[i]+2

  • 原式可化为: max ⁡ ( f [ j ] + d e p [ i ] + 2 , f [ i ] + d e p [ j ] + 2 ) \max(f[j]+dep[i]+2,f[i]+dep[j]+2) max(f[j]+dep[i]+2,f[i]+dep[j]+2)

  • 即为: f [ i ] − d e p [ i ] < f [ j ] − d e p [ j ] f[i]-dep[i]<f[j]-dep[j] f[i]−dep[i]<f[j]−dep[j](当选 j j j时成立)

因此在判断选取子节点时,可以先对子节点按照上述不等式排序,即为选取子节点的先后顺序。

最后需要注意的是,dfs过程中我们并没有判断根节点的时间,需要输出时进行判断取 m a x max max。

标程

cpp 复制代码
#include<bits/stdc++.h>

using namespace std;

#define IOS ios::sync_with_stdio(false); cin.tie(nullptr), cout.tie(nullptr);
#define int long long 
#define ULL unsigned long long 
#define PII pair<int, int>
#define lowbit(x) (x & -x)
#define Mid ((l + r) >> 1)
#define ALL(x) x.begin(), x.end()
#define endl '\n'
#define fi first 
#define se second

const int INF = 0x7fffffff;
const int Mod = 1e9 + 7;
const int N = 5e5 + 10;

int n;
vector<int> a[N];
int b[N], dep[N], f[N];

void dfs(int x, int y) {
    if(x != 1) f[x] = b[x];
    for(auto i : a[x]) {
        if(i == y) continue;
        dfs(i, x);
    }
    sort(ALL(a[x]), [](int n1, int n2) {
        return dep[n1] - f[n1] < dep[n2] - f[n2];
    });
    for(auto i : a[x]) {
        if(i == y) continue;
        f[x] = max(f[x], f[i] + dep[x] + 1);
        dep[x] += dep[i] + 2;
    }
}

void Solved() {
    cin >> n;
    for(int i = 1; i <= n; i ++ ) {
        cin >> b[i];
    }
    for(int i = 1; i < n; i ++ ) {
        int x, y; cin >> x >> y;
        a[x].push_back(y); a[y].push_back(x);
    }

    dfs(1, 0);
    
    cout << max(f[1], dep[1] + b[1]) << endl;
}

signed main(void) {
    IOS

    int ALL = 1; 
    // cin >> ALL;
    while(ALL -- ) Solved();
    // cout << fixed;//强制以小数形式显示
    // cout << setprecision(N); //保留n位小数

    return 0;
}
相关推荐
爱吃生蚝的于勒39 分钟前
C语言内存函数
c语言·开发语言·数据结构·c++·学习·算法
ChoSeitaku6 小时前
链表循环及差集相关算法题|判断循环双链表是否对称|两循环单链表合并成循环链表|使双向循环链表有序|单循环链表改双向循环链表|两链表的差集(C)
c语言·算法·链表
Fuxiao___6 小时前
不使用递归的决策树生成算法
算法
我爱工作&工作love我6 小时前
1435:【例题3】曲线 一本通 代替三分
c++·算法
白-胖-子7 小时前
【蓝桥等考C++真题】蓝桥杯等级考试C++组第13级L13真题原题(含答案)-统计数字
开发语言·c++·算法·蓝桥杯·等考·13级
workflower7 小时前
数据结构练习题和答案
数据结构·算法·链表·线性回归
好睡凯7 小时前
c++写一个死锁并且自己解锁
开发语言·c++·算法
Sunyanhui17 小时前
力扣 二叉树的直径-543
算法·leetcode·职场和发展
一个不喜欢and不会代码的码农7 小时前
力扣105:从先序和中序序列构造二叉树
数据结构·算法·leetcode
前端郭德纲7 小时前
浏览器是加载ES6模块的?
javascript·算法