【ALGO】最短路 Dijkstra

基本概念

Dijkstra算法用来解决只含有非负权图的单源最短路径问题(SSSP), 设定一个起始点node, 计算出该点到其他顶点的最短距离, 算法结束时会生成一棵最短路径树.

Dijkstra提出一个按路径长度不减次序生成最短路径的算法, 将图中顶点集合 V V V分成两组, 令 S S S表示已求出最短路径的顶点集合为A组, 其余尚未确定最短路径的顶点集合为B组。按照最短路径长度从小到大的顺序逐个将顶点加入到 S S S中, 直到从 n o d e node node出发的到达的顶点全部在 S S S中.

这个过程维持着以下几个性质:

  1. node S S S中各顶点的最短路径 < = <= <=node到B组中的最短路径.
  2. B组顶点的距离是node到此顶点的只包含 S S S中顶点为中间顶点的当前最短路径的长度.

算法正确性证明

类似Prim算法的贪心策略, 从起点node, 每次扩展一个B组中距离最短的点, 再以该点为中间点, 更新node到其他顶点的距离, 由于边权为非负值, 所以不会存在距离更短且未被扩展的点.

算法步骤

  1. 初始化dist[node]=0, node到其他顶点的距离dist[i]=INF.
  2. 找到未访问的点k(vis[k]=false), 满足dist[k]最小
  3. 标记点k
  4. k为中间节点, 修改node到其他未标记点j的距离值dist[j]
  5. 循环N轮后, 可以得到node到所有N个点的最短距离

代码模板

/*
时间复杂度: O(n^2)
输入: 
n 表示图中的点数
g g[i][j]表示i到j之间边的距离
输出:
dist dist[i]表示node到i的最短距离
*/
const int N=1005;
int dist[N];
int g[N][N];
bool vis[N];
void dijkstra(int n, int node){
    memset(vis, 0x00, sizeof vis);
    memset(dist, 0x3f, sizeof dist);
    dist(node)=0;

    for(int i=1; i<=n; ++i){ // loop n rounds
        int v=-1, mindis=0x3f3f3f3f;
        for(int j=1; j<=n; ++j) // 找到未访问的最小距离
            if(!vis[j] && dist[j]<mindis){
                mindis=dist[j];
                v=j;
            }
        if(v==-1) break;
        vis[v]=true;
        for(int j=1; j<=n; ++j){
            if(!vis[j]) dist[j]=min(dist[j], dist[v]+g[v][j]);
        }
    }
}

时间复杂度

算法需要循环 ∣ V ∣ |V| V轮, V V V表示顶点个数, 每次需要在B集合中查找距离最小值, 并对该点所连接的边进行扫描, 如果在维护最小值时使用堆(priority_queue), 即堆优化版本的Dijkstra算法, 复杂度为 O ( ( ∣ E ∣ + ∣ V ∣ ) l o g ∣ V ∣ ) O((|E|+|V|)log|V|) O((E+V)logV); 使用线性扫描法得到最小值, 时间复杂度为 O ( ∣ V ∣ 2 + ∣ E ∣ ) O(|V|^2+|E|) O(V2+E).
在最坏情况下, O ( ∣ E ∣ ) O(|E|) O(E) O ( ∣ V ∣ 2 ) O(|V|^2) O(V2)是同阶复杂度, 此时, 堆优化版本的Dijkstra算法的复杂度甚至还要高于线性版, 但是在一般情况下, 图中边的数量要显著小于顶点的数量 ∣ E ∣ < < ∣ V ∣ 2 |E|<<|V|^2 E<<V2, 即稀疏图(Sparse Graph), 堆优化版的Dijkstra算法有着显著的优势.

相关例题

洛谷: 【模板】单源最短路径(标准版)

洛谷: 【模板】单源最短路径(标准版)
给出一种堆优化版(小根堆存储)的Dijkstra解法如下

#include <bits/stdc++.h>
using namespace std;

typedef pair<int, int> PII;
#define fi first
#define se second

const int N=100005;
int n, m, s;
int dist[N];
bool vis[N];
vector<PII> g[N];

void dijkstra(){
    dist[s]=0;
    priority_queue<PII, vector<PII>, greater<PII>> q;
    q.push({0, s});
    while(!q.empty()){
        int node=q.top().se;
        q.pop();
        if(vis[node]) continue;
        vis[node]=true;
        for(auto &e: g[node]){
            int v=e.fi;
            int w=e.se;
            if(dist[v]>dist[node]+w){
                dist[v]=dist[node]+w;
                q.push({dist[v], v});
            }
        }
    }
}

int main(){
    memset(dist, 0x3f, sizeof dist);
    memset(vis, 0x00, sizeof vis);
    cin>>n>>m>>s;
    int x, y, w;
    while(m--){
        cin>>x>>y>>w;
        g[x].push_back({y, w});
    }
    
    dijkstra();
    for(int i=1; i<=n; ++i) cout<<dist[i]<<" ";
    
    return 0;
}

双端队列BFS

从堆优化版的Dijkstra的过程可以看出bfs算法的原型, 这种逐层搜索的方法可以解决从起始状态到每个状态需要的最少步数的问题, 可以等价于在一个边权均为1的图上执行BFS算法, 求出每个点相对于起点的最短距离(层次), 对于一类边权为0/1的问题, 可以考虑使用双端队列的BFS求解.

LeetCode 1368. 使网格图至少有一条有效路径的最小代价

LeetCode 1368. 使网格图至少有一条有效路径的最小代价
算法主体框架与BFS类似, 但是在扩展节点时略有不同, 如果该边的权值为0, 则将其加入队头; 如果该边的权值为1, 则将其加入队尾.

typedef pair<int, int> PII;
#define fi first;
#define se second;

class Solution {
public:
    int minCost(vector<vector<int>>& grid) {
        const int dirs[4][2]={{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
        int r=grid.size();
        int c=grid[0].size();
        int dist[r][c];
        bool vis[r][c];
        memset(dist, 0x3f, sizeof dist);
        memset(vis, 0x00, sizeof vis);
        dist[0][0]=0;
        deque<PII> q;
        q.push_back({dist[0][0], 0*c+0});
        vis[0][0]=true;
        while(!q.empty()){ // 双端队列BFS
            int node=q.front().se;
            int x=node/c;
            int y=node%c;
            q.pop_front();
            for(int i=0; i<4; ++i){
                int nx=x+dirs[i][0];
                int ny=y+dirs[i][1];
                bool cost=grid[x][y]-1!=i;
                if(0<=nx && nx<r && 0<=ny && ny<c && !vis[nx][ny]){
                    if(dist[nx][ny]>dist[x][y]+cost){
                        dist[nx][ny]=dist[x][y]+cost;
                        // 根据不同权重插入队列不同位置
                        if(cost) q.push_back({dist[nx][ny], nx*c+ny}); 
                        else q.push_front({dist[nx][ny], nx*c+ny});
                    }
                }
            }
        }
        return dist[r-1][c-1];
    }
};

参考文献

算法竞赛入门经典(第二版)
信息学奥赛一本通
ACM国际大学生程序设计竞赛 知识与入门/算法与实现

### Gradient Descent Algorithm Concepts The gradient descent algorithm involves several key terms and concepts before diving into its operation. One critical aspect is the **Parameter Update Rule**, which dictates how parameters are adjusted during each iteration to minimize the cost function[^1]. This rule ensures that the model learns by progressively moving toward lower error values. In addition, when discussing algorithm implementations such as those found in reinforcement learning methodologies, it becomes essential to define specific components like the state space, action space, and reward function. For instance, studies have implemented various algorithms including Q-Learning, Deep Q-Networks (DQN), and Proximal Policy Optimization (PPO)[^2]. These methods were tested within environments designed to simulate real-world scenarios closely resembling actual operational conditions. Here’s an example of parameter updates using Python code: ```python def update_parameters(learning_rate, gradients, params): updated_params = {} for param_name, grad_value in gradients.items(): current_param = params[param_name] new_param = current_param - learning_rate * grad_value updated_params[param_name] = new_param return updated_params ``` This snippet demonstrates a simple way to apply the parameter update rule based on computed gradients and specified learning rates. ### Reinforcement Learning Algorithms Implementation Example For reinforcement learning applications mentioned earlier, here's a basic structure illustrating how one might implement part of these processes programmatically with pseudo-code focusing particularly around DQNs: ```python import tensorflow as tf class DQNAgent: def __init__(self, state_size, action_size): self.state_size = state_size self.action_size = action_size # Define Neural Network Model Architecture Here... def train(self, batch_states, batch_actions, batch_rewards, next_batch_states): target_futures = self.model.predict(next_batch_states) targets = [] for i in range(len(batch_states)): future_reward = np.max(target_futures[i]) total_reward = batch_rewards[i] + gamma * future_reward target_vector = self.model.predict(np.array([batch_states[i]]))[0] target_vector[batch_actions[i]] = total_reward targets.append(target_vector) history = self.model.fit( np.array(batch_states), np.array(targets), epochs=epochs, verbose=False ) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Quant0xff

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值