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

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

2天前

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



原创内容 转载请注明出处

分享给朋友:

相关文章

力扣1137题:动态规划解泰波那契数 高效求解第N项的秘密

力扣1137题:动态规划解泰波那契数 高效求解第N项的秘密

一:重新解读题目泰波那契数列是一个充满数学趣味的递推序列:从第3项开始,每个数均为前三个数的和(即Tₙ₊₃ = Tₙ + Tₙ₊₁ + Tₙ₊₂)。当给定整数n时,需要高效计算出第n项的值。面对此类递...

从零到一掌握背包问题:洛谷P1164题解精讲,附带优化

从零到一掌握背包问题:洛谷P1164题解精讲,附带优化

题目重解:小A带着m元钱来到餐馆,菜单上有n道菜,每道菜都有确定的价格。现在需要计算出刚好花完m元的点菜方案总数。这个问题看似简单,但当菜品数量增多时,暴力枚举就会变得不可行,需要更高效的算法来解决。...

力扣501题最优解:不用额外空间找出BST中的众数?这个解法让你大开眼界

力扣501题最优解:不用额外空间找出BST中的众数?这个解法让你大开眼界

题目解读‌:在二叉搜索树的世界里,每个节点都默默记录着自己的数值。现在我们需要找出这些数值中出现频率最高的那些数字,也就是所谓的"众数"。有趣的是,二叉搜索树本身具有左小右大的特性...

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

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

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

LeetCode 120题三角形最小路径和最优解法:动态规划详解与代码实现

LeetCode 120题三角形最小路径和最优解法:动态规划详解与代码实现

一、题目解读LeetCode 120题“三角形最小路径和”要求给定一个由数字组成的三角形,从顶部开始向下移动,每次可向左或向右移动一格,计算从顶至底的最小路径和。三角形以二维向量形式给出,每层元素数量...

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

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

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

发表评论

访客

看不清,换一张

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