当前位置: 首页 > news >正文

Dijkstra 算法#图论

Dijkstra 算法

  • 算法前提:在没有负边的情况下使用。
  • 算法思路:将结点分成已确定最短路长度的点集 S 和未确定最短路长度的点集 T,每次从 T 集合中选取最短路长度最小的结点移到 S 集合中,并对其出边执行更新操作
  1. 从T集合中,选取一个最短路长度最小的结点,移到S集合中。
  2. 对那些刚刚被加入S集合的结点的所有出边执行松弛操作。

 

struct edge { int v, w; // 边的终点和权值
}; 
struct node { int dis, u; // 距离和顶点bool operator > (const node &a ) const {return dis>a.dis; // 优先队列比较函数,小根堆}
}; 
vector<edge> e[MAXN]; // 邻接表存储边
int dis[MAXN], vis[MAXN]; // dis存储最短距离,vis标记是否已确定最短距离
priority_queue<node, vector<node>, greater<node>> q; // 小根堆优先队列void dijkstra(int n, int s) {memset(dis, 0x3f, (n + 1) * sizeof(int)); // 初始化最短距离为无穷大memset(vis, 0, (n + 1) * sizeof(int)); // 初始化标记数组为0dis[s] = 0; // 起点到自身的距离为0q.push({0, s}); // 起点入队while (!q.empty()) {int u = q.top().u; // 取出距离最小的顶点q.pop(); if (vis[u]) continue; // 如果已确定最短距离,跳过vis[u] = 1; // 标记为已确定最短距离for (auto ed : e[u]) { // 遍历u的所有出边int v = ed.v, w = ed.w; // 边的终点和权值// 如果可以通过u得到更短的距离到vif (dis[v] > dis[u] + w) {dis[v] = dis[u] + w; // 更新最短距离q.push({dis[v], v}); // 将v入队}}}
}

 

 

 例题

#i#include <bits/stdc++.h>
using namespace std;const int MAXN = 2010;  // 最大节点数(人数),题目中 n≤2000,这里多开一点防止越界
double dis[MAXN];       // 存储从起点 A 到各节点能保留的金额比例,初始化为极小值
bool vis[MAXN];         // 标记节点是否已确定最长路径,避免重复处理
int n, m, A, B;         // n 是总人数,m 是转账对数,A 是转账起点,B 是转账终点// 边的结构体,用于构建图的邻接表,to 表示转账目标节点,rate 表示转账后能保留的比例(1 - 手续费比例)
struct Edge {int to;double rate;
};vector<Edge> graph[MAXN];  // 邻接表存储图结构,graph[u] 存所有从 u 出发能转账到的节点及对应保留比例// 优先队列中的节点结构体,用于 Dijkstra 算法,node 表示节点编号,val 表示到达该节点时能保留的金额比例
struct Node {int node;double val;// 重载小于运算符,让优先队列按照 val 从大到小排序(大顶堆),这样每次取出当前能保留比例最大的路径bool operator<(const Node& other) const {return val < other.val;}
};// Dijkstra 算法实现,求解从 A 出发到各节点能保留的最大金额比例
void dijkstra() {// 初始化 dis 数组,将起点 A 的保留比例设为 1(还没转账,金额完整保留),其他设为极小值fill(dis, dis + MAXN, 0);dis[A] = 1;// 优先队列,大顶堆,用于每次选当前能保留比例最大的路径拓展priority_queue<Node> pq;pq.push({A, 1});while (!pq.empty()) {Node cur = pq.top();pq.pop();int u = cur.node;double currentRate = cur.val;// 如果当前节点已经处理过(已确定最长路径),跳过if (vis[u]) continue;vis[u] = true;  // 标记为已处理// 遍历当前节点 u 的所有邻接边,尝试更新邻接节点的最大保留比例for (const Edge& e : graph[u]) {int v = e.to;double newRate = currentRate * e.rate;// 如果经过 u 转账到 v 能获得更大的保留比例,就更新,并将 v 加入队列等待处理if (newRate > dis[v]) {dis[v] = newRate;pq.push({v, newRate});}}}
}int main() {// 输入总人数 n 和转账对数 mscanf("%d%d", &n, &m);for (int i = 0; i < m; ++i) {int x, y, z;// 输入转账的两人 x、y 以及手续费比例 zscanf("%d%d%d", &x, &y, &z);double rate = 1 - z / 100.0;  // 计算转账后能保留的比例(1 - 手续费比例)// 因为是互相转账,所以添加两条有向边(x 到 y 和 y 到 x)graph[x].push_back({y, rate});graph[y].push_back({x, rate});}// 输入转账起点 A 和终点 Bscanf("%d%d", &A, &B);dijkstra();  // 执行 Dijkstra 算法,计算从 A 到各节点的最大保留比例// B 要收到 100 元,那么 A 需要的初始金额 = 100 / (A 到 B 能保留的比例)double result = 100 / dis[B];// 输出结果,精确到小数点后 8 位printf("%.8lf\n", result);return 0;
}
http://www.dtcms.com/a/265971.html

相关文章:

  • MySQL JSON数据类型完全指南:从版本演进到企业实践的深度对话
  • Windows 上使用 vscode + mingw 调试 python 程序
  • 国内MCP服务平台推荐!aibase.cn上线MCP服务器集合平台
  • 二叉树的右视图C++
  • MySQL的窗口函数介绍
  • 每日算法刷题Day41 6.28:leetcode前缀和2道题,用时1h20min(要加快)
  • golang json omitempty 标签研究
  • 图论基础算法入门笔记
  • OOM电商系统订单缓存泄漏,这是泄漏还是溢出
  • 免费一键自动化申请、续期、部署、监控所有 SSL/TLS 证书,ALLinSSL开源免费的 SSL 证书自动化管理平台
  • 【进阶篇-消息队列】——RocketMQ如何实现事务的
  • HarmonyOS ArkTS卡片堆叠滑动组件实战与原理详解(含源码)
  • 如何挖掘客户的隐性需求
  • 474. 一和零
  • 【华为od刷题(C++)】HJ22 汽水瓶
  • ubuntu22 桌面版开启root登陆
  • ubuntu22.04安装anaconda
  • embbding you should know
  • 独立开发者软件出海:如何用Semrush高效洞察与增长
  • 【Note】《深入理解Linux内核》Chapter 10 :Linux 内核中的系统调用机制全解析
  • 贝叶斯深度学习:赋予AI不确定性感知的认知革命
  • 【Oracle学习笔记】8.函数(Function)
  • 湖北理元理律师事务所的债务管理方法论
  • 算法刷题打卡(1)—— 快速排序
  • 睿尔曼系列机器人——以创新驱动未来,重塑智能协作新生态(上)
  • 【python】OOP:Object-Oriented Programming
  • 数字人分身+矩阵系统聚合+碰一碰发视频: 源码搭建-支持OEM
  • AI开发平台:从技术壁垒到全民创新,AI 开发平台如何重构产业生态?
  • C++ 标准模板库算法之 transform 用法
  • STC8G 8051内核单片机开发 (中断)