当前位置:首页 > 提高组 > (2018年NOIP提高组)洛谷P5021题:二分查找+动态规划解决赛道修建

(2018年NOIP提高组)洛谷P5021题:二分查找+动态规划解决赛道修建

7个月前 (08-20)

(2018年NOIP提高组)洛谷P5021题:二分查找+动态规划解决赛道修建 图论算法 二分查找 动态规划 DFS 深度优先搜索 深搜 递归 树结构 NOIP 提高组 第1张

一、题目解读

洛谷P5021题(2018年NOIP提高组)要求在一棵带权中,查找满足路径权重大于等于指定阈值的最小阈值,同时保证符合条件的路径数量达到给定目标。问题本质是图论中的路径计数与二分查找的结合,需通过动态规划思想优化求解过程。

二、解题思路

采用二分查找阈值,将问题转化为“检查是否存在至少m条路径权重大于等于mid”。核心逻辑为:

1. 通过DFS遍历树,递归计算子树中以各节点为起点的最大路径权重;

2. 利用multiset存储未达标路径,通过二分匹配查找互补路径对,统计满足条件的路径数;

3. 动态调整阈值区间,直至找到符合条件的最小阈值。

三、解题步骤

1. 建与数据预处理:用vector存储树边信息,记录每条边的权重,初始化左右边界(路径权重范围);

2. 二分查找阈值:通过mid = (left + right) / 2迭代,调用DFS检查路径数量;

3. DFS递归计算:

○ 遍历当前节点的子树,递归获取子节点的最大路径权值(含边权);

○ 若子路径≥mid则计入cnt,否则存入multiset;

○ 通过multiset动态匹配路径对,更新cnt并维护最长单路径;

4. 阈值判定与收敛:若cnt≥m则左移区间(缩小阈值),反之右移,最终获取最小合法阈值。

四、代码与注释

#include <iostream>
#include <vector>
#include <algorithm>
#include<set>
using namespace std;

const int MAXN = 50010;
vector<pair<int, int>> tree[MAXN]; // 存储树边:节点-邻接点, 权重
int n, m, cnt; // n:节点数, m:目标路径数, cnt:当前合法路径计数

// 二分检查函数
int dfs(int u, int fa, int mid) { // u:当前节点, fa:父节点, mid:阈值
    multiset<int> s; // 存储未达标路径
    for (auto &edge : tree[u]) { // 遍历子树
        int v = edge.first, w = edge.second; // 邻接点v, 边权w
        if (v == fa) continue; // 跳过父节点
        int val = dfs(v, u, mid) + w; // 递归获取子树最大路径 + 边权
        if (val >= mid) cnt++; // 若≥阈值则计数
        else s.insert(val); // 否则存入multiset
    }
    
    int max_path = 0; // 记录当前节点最长单路径
    while (!s.empty()) { // 动态匹配路径对
        auto it = s.begin(); // 取最小未达标路径
        int x = *it;
        s.erase(it);
        auto match = s.lower_bound(mid - x); // 查找互补路径
        if (match!= s.end()) { // 若存在匹配
            cnt++; // 计数
            s.erase(match); // 删除配对路径
        } else { // 无匹配则更新最长单路径
            max_path = max(max_path, x);
        }
    }
    return max_path; // 返回当前节点最长单路径(用于后续递归)
}

int main() {
    cin >> n >> m; // 输入节点数n, 目标路径数m
    int left = 0, right = 0; // 初始化阈值区间
    for (int i = 1; i < n; i++) { // 建图
        int a, b, l;
        cin >> a >> b >> l; // 输入边信息
        tree[a].emplace_back(b, l); // 双向边
        tree[b].emplace_back(a, l);
        right += l; // 更新右边界(最大路径权)
    }
    
    int ans = 0; // 最终答案
    while (left <= right) { // 二分查找
        int mid = (left + right) / 2;
        cnt = 0; // 重置计数
        dfs(1, 0, mid); // 从根节点开始检查
        if (cnt >= m) { // 若路径数达标
            ans = mid; // 记录答案
            left = mid + 1; // 左移区间(缩小阈值)
        } else {
            right = mid - 1; // 右移区间(增大阈值)
        }
    }
    cout << ans << endl; // 输出结果
    return 0;
}

五、总结

该解法巧妙将路径阈值判定转化为二分查找,结合multiset动态匹配路径对,避免了暴力枚举的指数级复杂度。核心优化在于利用递归子树信息快速统计合法路径数,并通过区间收敛精准定位最小阈值。算法时间复杂度为O(nlogn),空间复杂度为O(n),适用于大规模图论场景。



原创内容 转载请注明出处

分享给朋友:

相关文章

力扣654:递归分治的艺术 如何用最大元素构建二叉树

力扣654:递归分治的艺术 如何用最大元素构建二叉树

题目重解我们面对一个看似简单却充满递归魅力的题目:给定一个不含重复元素的整数数组,需要构建一棵特殊的二叉树。这个树的每个父节点都必须是当前子数组中的最大元素,而它的左右子树则分别由该最大值左侧和右侧的...

IOI 1994 洛谷1216:如何用O(1)空间解决数字三角形问题?附代码实现

IOI 1994 洛谷1216:如何用O(1)空间解决数字三角形问题?附代码实现

题目重解:数字三角形是一个经典的动态规划问题,给定一个由数字组成的三角形结构,从顶部出发,每次可以移动到下方相邻的数字,最终到达底部。我们需要找到一条路径,使得路径上经过的数字总和最大。这个问题可以很...

2024年GESP五级武器强化(洛谷B4071)解题代码C++版

2024年GESP五级武器强化(洛谷B4071)解题代码C++版

一、题目解读    2024年GESP(青少年软件编程能力等级考试)五级中的“武器强化”(洛谷平台题目编号B4071)是一道典型的算法优化问题。题目要求通过合理...

CSP-J 2019纪念品题解(洛谷P5662):动态规划+完全背包问题的实战应用

CSP-J 2019纪念品题解(洛谷P5662):动态规划+完全背包问题的实战应用

一、题目解读2019年CSP-J的“纪念品”问题(对应洛谷P5662)要求玩家在T天内通过买卖纪念品最大化金币收益。每天可交易N种商品,需计算最优策略下的最终金币数。题目强调动态规划思维与资源分配优化...

NOIP 2008火柴棒等式题解(C++代码实现)  动态规划与枚举算法详解

NOIP 2008火柴棒等式题解(C++代码实现) 动态规划与枚举算法详解

一、题目解读火柴棒等式问题(NOIP 2008,洛谷P1149)要求使用给定数量的火柴棒,构造形如 A + B = C 的等式,其中A、B、C均为整数,且火柴棒总数恰好等于输入值。需统计符合条件的等式...

2024蓝桥杯省赛B组“传送阵”题解(C++代码+图论算法优化)

2024蓝桥杯省赛B组“传送阵”题解(C++代码+图论算法优化)

一、题目解读2024年蓝桥杯省B组“传送阵”题目要求处理一个包含n个节点的图,节点间存在单向传输关系。每个节点i可传送至a[i]指定的节点,形成可能存在的环结构。题目需求解从任意节点出发能到达的最长路...

发表评论

访客

看不清,换一张

◎欢迎参与讨论,请在这里发表您的看法和观点。