PAT1003 迪杰斯特拉(Dijkstra)

46 篇文章 0 订阅

算法简介

迪杰斯特拉(Dijkstra)是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是从起始点开始,采用贪心算法的策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止。

讲白了,就是给你一张有很多城市的地图,要你求指定两个城市之间的最小距离。

基本思想

带权有向图.png

给定一个上述图,要求某两个点之间的最短路径,这就叫做 有权图单源最短路 。如果是要求任意两个点之间的最短路径,这就是 有权图无源最短路径 ,无源最短路径用 Dijkstra 的时间复杂度就上去了,一般用 Floyd 算法了。

Dijkstra 理解不难,一起模拟一遍就懂了。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

造轮子

利用的c++的 pair和prority_queue 实现 Dijkstra

c++模板

struct cmp {
  bool operator()(const pair<int, int> &a, const pair<int, int> &b) {
    return a.first > b.first;
  }
};

class Dijkstra {
 private:
  int n;
  vector<vector<pair<int, int>>> v;

 public:
  void Init(int n) {
    this->n = n;
    v.clear();
    v.resize(n + 1);
    for (int i = 1; i <= n; i++) {
      v[i].clear();
    }
  }

  void Add(int a, int b, int c) {
    v[a].push_back(make_pair(b, c));
    // 如果是双向的,加上下面这一句。
    // v[b].push_back(make_pair(a, c));
  }

  void GetDist(int st, vector<int> &dist, vector<int> &path) {
    for (int i = 1; i <= n; ++i) {
      path[i] = -1;
    }
    for (int i = 1; i <= n; ++i) {
      dist[i] = INT_MAX;
    }
    priority_queue<pair<int, int>, vector<pair<int, int>>, cmp> q;
    dist[st] = 0;
    q.push(make_pair(0, st));
    while (!q.empty()) {
      int now = q.top().second;
      if (dist[now] != q.top().first) {
        q.pop();
        continue;
      }
      q.pop();
      for (int i = 0; i < (int)v[now].size(); i++) {
        int next = v[now][i].first, value = v[now][i].second;
        if (dist[now] + value < dist[next]) {
          dist[next] = dist[now] + value;
          q.push(make_pair(dist[next], next));
          path[next] = now;
        }
      }
    }
  }
};

参考了网上一位大神的写法。bilibili主页地址

测试代码

#include <algorithm>
#include <iostream>
#include <queue>
#include <stack>
#include <utility>
#include <vector>

using namespace std;

class Solution {
 public:
  pair<int, vector<int>> solve(int n, vector<vector<int>> &edges,
                               vector<int> &succProb, int start, int end) {
    Dijkstra d;
    d.Init(n);
    int size = edges.size();
    for (int i = 0; i < size; ++i) {
      int a = edges[i][0], b = edges[i][1];
      int c = succProb[i];
      d.Add(a, b, c);
    }
    vector<int> dist(n + 1);
    vector<int> path(n + 1);
    d.GetDist(start, dist, path);
    return make_pair(dist[end], path);
  }

  void printWay(const vector<int> &path, int end) {
    stack<int> s;
    while (path[end] != -1) {
      s.push(path[end]);
      end = path[end];
    }
    while (!s.empty()) {
      cout << s.top() << " ";
      s.pop();
    }
    cout << endl;
  }
};

int main() {
  vector<vector<int>> edges;
  edges.push_back({1, 2});  // 2
  edges.push_back({1, 4});  // 1
  edges.push_back({2, 5});  // 10
  edges.push_back({2, 4});  // 3
  edges.push_back({3, 1});  // 4
  edges.push_back({3, 6});  // 5
  edges.push_back({4, 3});  // 2
  edges.push_back({4, 5});  // 2
  edges.push_back({4, 6});  // 8
  edges.push_back({4, 7});  // 4
  edges.push_back({5, 7});  // 6
  edges.push_back({7, 6});  // 1
  vector<int> succProb;
  succProb.emplace_back(2);
  succProb.emplace_back(1);
  succProb.emplace_back(10);
  succProb.emplace_back(3);
  succProb.emplace_back(4);
  succProb.emplace_back(5);
  succProb.emplace_back(2);
  succProb.emplace_back(2);
  succProb.emplace_back(8);
  succProb.emplace_back(4);
  succProb.emplace_back(6);
  succProb.emplace_back(1);
  Solution s;
  auto ans1 = s.solve(7, edges, succProb, 1, 6);
  cout << "1->6最短路径长度是: " << ans1.first << endl;
  cout << "路径是: ";
  s.printWay(ans1.second, 6);
  ans1 = s.solve(7, edges, succProb, 1, 2);
  cout << "1->2最短路径长度是: " << ans1.first << endl;
  cout << "路径是: ";
  s.printWay(ans1.second, 2);
  ans1 = s.solve(7, edges, succProb, 1, 3);
  cout << "1->3最短路径长度是: " << ans1.first << endl;
  cout << "路径是: ";
  s.printWay(ans1.second, 3);
  ans1 = s.solve(7, edges, succProb, 1, 4);
  cout << "1->4最短路径长度是: " << ans1.first << endl;
  cout << "路径是: ";
  s.printWay(ans1.second, 4);
  ans1 = s.solve(7, edges, succProb, 1, 5);
  cout << "1->5最短路径长度是: " << ans1.first << endl;
  cout << "路径是: ";
  s.printWay(ans1.second, 5);
  ans1 = s.solve(7, edges, succProb, 1, 6);
  cout << "1->6最短路径长度是: " << ans1.first << endl;
  cout << "路径是: ";
  s.printWay(ans1.second, 6);
  ans1 = s.solve(7, edges, succProb, 1, 7);
  cout << "1->7最短路径长度是: " << ans1.first << endl;
  cout << "路径是: ";
  s.printWay(ans1.second, 7);
  return 0;
}

变种

PAT 1003 Emergency

As an emergency rescue team leader of a city, you are given a special map of your country. The map shows several scattered cities connected by some roads. Amount of rescue teams in each city and the length of each road between any pair of cities are marked on the map. When there is an emergency call to you from some other city, your job is to lead your men to the place as quickly as possible, and at the mean time, call up as many hands on the way as possible.

Input Specification:

Each input file contains one test case. For each test case, the first line contains 4 positive integers: N (≤500) - the number of cities (and the cities are numbered from 0 to N−1), M - the number of roads, C1 and C2 - the cities that you are currently in and that you must save, respectively. The next line contains N integers, where the i-th integer is the number of rescue teams in the i-th city. Then M lines follow, each describes a road with three integers c1, c2 and L, which are the pair of cities connected by a road and the length of that road, respectively. It is guaranteed that there exists at least one path from C1 to C2.

Output Specification:

For each test case, print in one line two numbers: the number of different shortest paths between C1 and C2, and the maximum amount of rescue teams you can possibly gather. All the numbers in a line must be separated by exactly one space, and there is no extra space allowed at the end of a line.

这题就是一道 Dijkstra 的变种题目。

题意:作为城市搜救队的队长,你有一张特殊的地图,这张地图展示了由几条道路连接起来的几个分散城市。地图上标注了每个城市的救援队和每队城市之间的距离。当你收到城市的紧急电话后,你的工作是带领你的人尽快的赶到目的地,在此基础上召集更多的救援队。

输入:第一行由4个数字,第一个数字表示有几个城市N,第二个数字表示有几条道路M,第三个城市是起点城市C1,第四个是终点城市C2,第二行有N个数字,第i个数字表示第i个城市所用的救援队数量。接下来M行表示c1城市到c2城市的距离L。

输出:输出最短距离的路径条数和在最短距离的前提下,救援队的最大数量。

这里不是单向图,两个城市可以互通的。

设置两个向量,nummaxPeople 分别表示最短路径条数和救援队最大人数。vec 每个城市的救援队的数量。

只需要多判断 dist[now] + value = dist[next] 的情况就好了。

dist[now] + value = dist[next] 意味着,最短路径多了一条,我们就用 num[next] += num[now] ,如果 maxPeople[now] + vec[next] > maxPeople[next] ,说明我这条最短路径的救援队人数多,就更新 maxPeople 的值。

dist[now] + value < dist[next] 时,最短路径保持不变,也就是 num[next] = num[now] ,但是救援队人数我们还是要累加的 maxPeople[next] = maxPeople[now] + vec[next]

#include <algorithm>
#include <iostream>
#include <queue>
#include <stack>
#include <utility>
#include <vector>

using namespace std;

struct cmp {
  bool operator()(pair<int, int> &a, pair<int, int> &b) {
    return a.first > b.first;
  }
};

class Dijkstra {
 private:
  int n;
  vector<vector<pair<int, int>>> v;

 public:
  void Init(int n) {
    this->n = n;
    v.clear();
    v.resize(n);
    for (int i = 0; i < n; ++i) {
      v[i].clear();
    }
  }

  void Add(int a, int b, int c) {
    v[a].push_back(make_pair(b, c));
    v[b].push_back(make_pair(a, c));
  }

  void GetDist(int st, vector<int> &dist, vector<int> &path,
               const vector<int> &vec, vector<int> &maxPeople,
               vector<int> &num) {
    for (int i = 0; i < n; ++i) {
      maxPeople[i] = 0;
    }
    for (int i = 0; i < n; ++i) {
      num[i] = 0;
    }
    maxPeople[st] = vec[st];
    num[st] = 1;
    for (int i = 0; i < n; ++i) {
      path[i] = -1;
    }
    for (int i = 0; i < n; ++i) {
      dist[i] = 2147483647;
    }
    priority_queue<pair<int, int>, vector<pair<int, int>>, cmp> q;
    dist[st] = 0;
    q.push(make_pair(0, st));
    while (!q.empty()) {
      int now = q.top().second;
      if (dist[now] != q.top().first) {
        q.pop();
        continue;
      }
      q.pop();
      for (int i = 0; i < (int)v[now].size(); i++) {
        int next = v[now][i].first, value = v[now][i].second;
        if (dist[now] + value < dist[next]) {
          dist[next] = dist[now] + value;
          q.push(make_pair(dist[next], next));
          path[next] = now;
          num[next] = num[now];
          maxPeople[next] = maxPeople[now] + vec[next];
        } else if (dist[now] + value == dist[next]) {
          num[next] += num[now];
          if (maxPeople[now] + vec[next] > maxPeople[next]) {
            maxPeople[next] = maxPeople[now] + vec[next];
          }
        }
      }
    }
  }
};

int main() {
  int n, m, start, end;
  cin >> n >> m >> start >> end;
  vector<int> vec;
  for (int i = 0; i < n; ++i) {
    int temp;
    cin >> temp;
    vec.emplace_back(temp);
  }
  vector<vector<int>> edges;
  vector<int> succProb;
  for (int i = 0; i < m; ++i) {
    int c1, c2, c3;
    cin >> c1 >> c2 >> c3;
    edges.push_back({c1, c2});
    succProb.emplace_back(c3);
  }
  int size = edges.size();
  Dijkstra d;
  d.Init(n);
  for (int i = 0; i < size; ++i) {
    int a = edges[i][0], b = edges[i][1];
    int c = succProb[i];
    d.Add(a, b, c);
  }
  vector<int> dist(n);
  vector<int> path(n);
  vector<int> num(n);
  vector<int> maxPeople(n);
  d.GetDist(start, dist, path, vec, maxPeople, num);
  cout << num[end] << " " << maxPeople[end] << endl;
  return 0;
}

嫖了几个测试用例

6 9 0 5

1 2 1 5 3 4

0 1 1

0 2 2

0 3 1

1 2 1

2 3 1

2 4 1

2 5 1

3 4 1

4 5 1

6 9 0 2

1 2 1 5 3 4

0 1 1

0 2 2

0 3 1

1 2 1

2 3 1

2 4 1

2 5 1

3 4 1

4 5 1

6 9 0 4

1 2 1 5 3 4

0 1 1

0 2 2

0 3 1

1 2 1

2 3 1

2 4 1

2 5 1

3 4 1

4 5 1

结果分别是:

4 13

3 7

1 9

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值