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

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

2个月前 (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),适用于大规模图论场景。



原创内容 转载请注明出处

分享给朋友:

相关文章

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

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

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

牛客12576题解题全解析:动态规划+质因数分解实现跳跃问题最优解

牛客12576题解题全解析:动态规划+质因数分解实现跳跃问题最优解

一、题目解读牛客12576题是一道经典的算法题,要求给定起点N和终点M,求解从N到M的最少跳跃次数。题目考察的核心在于路径优化与动态规划思想,需结合数论中的质因数分解技巧,通过合理设计算法降低时间复杂...

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

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

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

【CSP-S 2019】括号树(洛谷P5658)解题报告:栈+DFS+异或优化详解

【CSP-S 2019】括号树(洛谷P5658)解题报告:栈+DFS+异或优化详解

一、题目解读括号树问题(洛谷P5658)要求处理一个由括号序列转化的树结构:每个节点表示一个括号,'('为子节点,')'为父节点。题目给定一棵n个节点的树,需计算每个节...

洛谷P4551题解题报告:图论与Trie树优化异或路径问题的实战解析

洛谷P4551题解题报告:图论与Trie树优化异或路径问题的实战解析

一、题目解读洛谷P4551题要求在一个无向图中,寻找任意两点路径权值异或后的最大值。题目输入为图的边信息(点数n和n-1条边),每条边包含起点、终点及权值。需输出所有路径中权值异或的最大值。问题核心在...

CSP-J 2019公交换乘题解析:基于队列优化的动态规划代码详解

CSP-J 2019公交换乘题解析:基于队列优化的动态规划代码详解

一、题目解读CSP-J 2019年的“公交换乘”题目(洛谷P5661)要求模拟地铁与公交交替出行的费用计算。题目核心在于地铁消费会产生优惠券,而公交可在45分钟内使用优惠券抵扣车费。需要处理n条出行记...

发表评论

访客

看不清,换一张

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