image.png

Dijkstra算法邻接矩阵代码模板 O(n2)

适合图的点数不超过1000的情况

  1. //全局变量部分
  2. const int MAXN = 1000; //图的最大顶点数
  3. const int INF = 1000000000; //设立INF为一个极大数,
  4. //这里这个数也可以设置为0x3f3f3f3f,这代表无穷大,即1061109567这一数值
  5. int n; //当前图的点数
  6. int vis[MAXN]; //标记数组用于记录图中各个点的被访问情况,0为未访问,1为已访问
  7. int dis[MAXN]; //记录起点到各个顶点的最短路径长度
  8. int m[MAXN][MAXN]; //用一个邻接矩阵来记录图中各个点的连接情况
  9. void Dijkstra(int s){ //s为起点
  10. fill(vis, vis+MAXN, 0); //将标记数组初始化为0即未被访问状态,此处可用memset
  11. fill(dis, dis+MAXN, INF); //将最短路数组初始化为一个很大的数,此处要注意不可用memset
  12. dis[s]=0; //首先将起点s到达自身的最短路设为0
  13. for(int i=0;i<n;i++){ //n次循环,遍历完n个点
  14. int node = -1; //node记录当前能找到的从起点到此没被访问的最短的一个点
  15. int minn = INF; //minn记录到达那个点的最短路径
  16. for(int j=0;j<n;j++){ //每个点逐步寻找没被访问的最短的一个点
  17. if(vis[j]==0 && dis[j]<minn){
  18. minn = dis[j];
  19. node = j;
  20. }
  21. }
  22. if(node==-1){ //找不到小于INF的一个点,说明其他结点与定点不连通
  23. return ;
  24. }
  25. vis[node] = 1; //将当前点标记为1
  26. for(int j=0;j<n;j++){
  27. if(vis[j]==0 && m[node][j]!=INF && (dis[node]+m[node][j])<dis[j]){
  28. //如果结点未被访问且 node能到达此结点 并且从起点到此结点过node中介点更近
  29. //在此标尺只有距离,如果有两条距离相等或多条相等,那么就只需在此进行修改
  30. //如路径相同第二标尺为花费时,则可通过在增加花费数组,在此if后加elseif判断距离相等情况花费不同条件。
  31. //下题就有两标尺。
  32. dis[j] = dis[node]+m[node][j];
  33. }
  34. }
  35. }
  36. }

二维矩阵+堆优化:个人认为最好写最实用

总结一下,Dijkstra算法的流程就是,不断取出离顶点最近没有被访问过的点,松弛它和它能到达的所有点。
如何取出离顶点最近的点?如果暴力寻找,那就是朴素的Dijkstra算法,时间复杂度是O^2
但我们可以采取堆优化。具体而言,我们可以用一个优先队列(或手写堆,那样更快)来维护所有节点。这样可以实现 mlogm 的算法

  1. int dis[maxn], vis[maxn] = {0};
  2. vector<pair<int, int>> E[maxn];//存图 <u, d> E[i]到u点距离为d
  3. typeof pair<int ,int> PAIR; //<起点到x点的距离, x>
  4. #define MP make_pair
  5. priority_queue<PAIR, vector<PAIR>, greater<PAIR>> Q;//距离短的优先级高,也就是 Q.top()
  6. void Dij(int s){
  7. fill(dis, dis + maxn, INT_MAX);
  8. fill(vis, vis + maxn, 0);
  9. dis[s] = 0;
  10. Q.push(MP(0,s));
  11. while(!Q.empty()){
  12. int u = Q.top().second;Q.pop();
  13. if(vis[u])continue;
  14. vis[u] = 1;
  15. for(auto it : E[u]){
  16. if(dis[it.first] > it.second + dis[u]){
  17. dis[it.first] = it.second + dis[u];//更新dis
  18. //或许还有其他的操作
  19. }else if(){}//有时还有其他的判断条件
  20. if(!vis[it.first]) Q.push(MP(dis[it.first], it.first));
  21. }
  22. }
  23. }

优化:链式前向星+堆优化 O(mlogm)

链式前向星
用一个优先队列(或手写堆,那样更快)来维护所有节点。

  1. typedef pair<int, int> Pair; //<x点到起点的距离, x>
  2. priority_queue<Pair, vector<Pair>, greater<Pair> > Q; // // 这里是greater, 使得距离短的先出队
  3. void Dij(int s) //传入起点编号
  4. {
  5. fill(vis, vis+MAXN, 0);
  6. fill(dis, dis+MAXN, INF);
  7. dist[s] = 0;
  8. Q.push(make_pair(0, s));
  9. while (!Q.empty())
  10. {
  11. int p = Q.top().second;
  12. Q.pop();
  13. if (vis[p])
  14. continue;
  15. vis[p] = 1;
  16. for (int e = head[p]; e != 0; e = edges[e].next)
  17. {
  18. int to = edges[e].to;
  19. dist[to] = min(dist[to], dist[p] + edges[e].w);
  20. if (!vis[to])
  21. Q.push(make_pair(dist[to], to));
  22. }
  23. }
  24. }

题目

L2-001 紧急救援 (25 分)

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int INF = 1e8;
  4. //N<500
  5. int saveTeam[505];//各点的存储的救援人数
  6. int st[505];//按路径能召集到的最多人数
  7. int mm[505][505];//地图
  8. int number[505];//到该点最短路径的数量
  9. int vis[505];//是否标记为node过
  10. int dis[505];//起点到该点的距离
  11. int father[505];//最短路径中,该点的上一个节点
  12. int n,m,s,d;//城市数量、路线数量、起点、终点
  13. void Dijkstra(){
  14. //初始化
  15. fill(dis,dis+n,INF);
  16. fill(vis,vis+n,0);
  17. fill(number,number+5,0);
  18. dis[s]=0;
  19. st[s]=saveTeam[s];
  20. number[s]=1;
  21. for(int i=0; i<n;i++){
  22. int minn = INF,node = -1;
  23. for(int j =0;j<n;j++){
  24. if(!vis[j] && dis[j]<minn){
  25. minn = dis[j];
  26. node = j;
  27. }
  28. }
  29. vis[node]=1;
  30. if(node == -1)return;
  31. for(int j =0;j<n;j++){
  32. if(vis[j]==0 && mm[node][j]!=-1 && (dis[node]+mm[node][j]) <dis[j]){
  33. dis[j]=dis[node]+mm[node][j];
  34. st[j] = st[node]+saveTeam[j];
  35. father[j]=node;
  36. number[j] = number[node];
  37. }else if(vis[j]==0 && mm[node][j]!=-1 && (dis[node]+mm[node][j])==dis[j]){
  38. if((st[node]+saveTeam[j]) > st[j]){
  39. st[j] = st[node]+saveTeam[j];
  40. father[j]=node;
  41. }
  42. number[j]+=number[node];
  43. }
  44. }
  45. }
  46. }
  47. int main(){
  48. vector<int> pathV;
  49. cin>>n>>m>>s>>d;
  50. for(int i = 0;i<n;i++){
  51. cin>>saveTeam[i];
  52. }
  53. //初始化地图
  54. for(int i = 0;i<n;i++){
  55. for(int j = 0;j<n;j++){
  56. mm[i][j] = -1;
  57. }
  58. }
  59. //读入地图
  60. for(int i =0;i<m;i++){
  61. int n1,n2,val;
  62. cin>>n1>>n2>>val;
  63. mm[n1][n2]=val;
  64. mm[n2][n1]=val;
  65. }
  66. Dijkstra();
  67. int p =d;
  68. while(1){
  69. if(p==father[p]){//应该是0,即没有father
  70. // cout<<"pfather"<<p<<" "<<father[p]<<"\n";
  71. pathV.push_back(p);break;
  72. }
  73. pathV.push_back(p);
  74. // cout<<"pfather"<<p<<" "<<father[p]<<"\n";
  75. p = father[p];
  76. }
  77. cout<<number[d]<<" "<<st[d]<<endl;
  78. // cout<<pathV.size();
  79. for(int i = pathV.size()-1; i>=0;i--){
  80. if(i==pathV.size()-1){
  81. cout<<pathV[i];
  82. }else{
  83. cout<<" "<<pathV[i];
  84. }
  85. }
  86. }

L3-005 垃圾箱分布 (30 分)

题目大意:

  • 从m个垃圾站里面选取1个站点,让他离居民区的最近的人最远,并且没有超出服务范围ds之内。
  • 如果有很多个最远的垃圾站,输出距离所有居民区距离平均值最小的那个。
  • 如果平均值还是一样,就输出按照顺序排列垃圾站编号最小的那个

思路:

  • 垃圾站之间也是彼此有路连接的,最短路径计算也要把垃圾站算上。
  • 所以我们就是对 n+m 个点进行Dijkstra计算最短路径,计算出1~m号垃圾站距离其他站点的最短路径。
  • 遍历dis数组,如果dis存在一个距离大于服务范围ds的距离,那么我们就舍弃这个垃圾站。
  • 取最短的路径,这就是距离它最近的垃圾站mindis。如果mindis > ansdis,更新。
  • G开头的,编号设为n+G后面的数字

    1. #include <cstdio>
    2. #include <algorithm>
    3. #include <iostream>
    4. #include <string>
    5. using namespace std;
    6. const int inf = 999999999;
    7. int n, m, k, ds, station;
    8. int e[1020][1020], dis[1020];
    9. bool visit[1020];
    10. int main() {
    11. //初始化地图,没路的距离就是inf
    12. fill(e[0], e[0] + 1020 * 1020, inf);
    13. fill(dis, dis + 1020, inf);
    14. scanf("%d%d%d%d", &n, &m, &k, &ds);
    15. for(int i = 0; i < k; i++) {
    16. int tempdis;
    17. string s, t;
    18. cin >> s >> t >> tempdis;
    19. int a, b;
    20. //注意处理字符串//这样处理最后一个点会错误——没注意是小于“等于”10情况...
    21. // if(s[0] == 'G') {
    22. // a = s[1] - '0' + n;
    23. // } else {
    24. // a = stoi(s);
    25. // }
    26. // if(t[0] == 'G') {
    27. // b = t[1] - '0' + n;
    28. // } else {
    29. // b = stoi(t);
    30. // }
    31. if(s[0] == 'G') {
    32. s = s.substr(1);
    33. a = n + stoi(s);
    34. } else {
    35. a = stoi(s);
    36. }
    37. if(t[0] == 'G') {
    38. t = t.substr(1);
    39. b = n + stoi(t);
    40. } else {
    41. b = stoi(t);
    42. }
    43. //将数据添入地图
    44. e[a][b] = tempdis;
    45. e[b][a] = tempdis;
    46. }
    47. int ansid = -1;//存储最终选择的候选编号,如果没有符合要求的 就是-1
    48. double ansdis = -1, //最终的最短路径
    49. ansaver = inf;//最终的平均路径长度
    50. //对每个候选点进行dij算法:将其作为起点,算出到各个居民的距离——存储在dis中
    51. for(int index = n + 1; index <= n + m; index++) {
    52. double mindis = inf, //候选点到各个居民的路径中最短的长度
    53. aver = 0; //该候选点到各点的平均长度
    54. fill(dis, dis + 1020, inf); //重置dis
    55. fill(visit, visit + 1020, false); //重置vis
    56. dis[index] = 0; //起点到自身距离自然为0
    57. for(int i = 0; i < n + m; i++) {
    58. int u = -1, //本轮距离起点最近的点
    59. minn = inf;//最近距离
    60. for(int j = 1; j <= n + m; j++) {
    61. if(visit[j] == false && dis[j] < minn) {
    62. u = j;
    63. minn = dis[j];
    64. }
    65. }
    66. if(u == -1) break;//没其他点能到起点了
    67. visit[u] = true;
    68. for(int v = 1; v <= n + m; v++) {
    69. if(visit[v] == false && dis[v] > dis[u] + e[u][v])
    70. dis[v] = dis[u] + e[u][v];
    71. }
    72. }
    73. for(int i = 1; i <= n; i++) {
    74. if(dis[i] > ds) { //有距离过远,不符合题意
    75. mindis = -1;
    76. break;
    77. }
    78. if(dis[i] < mindis) mindis = dis[i]; //最短路径
    79. aver += 1.0 * dis[i];
    80. }
    81. if(mindis == -1) continue;
    82. aver = aver / n;
    83. //判断一下
    84. if(mindis > ansdis) {
    85. ansid = index;
    86. ansdis = mindis;
    87. ansaver = aver;
    88. } else if(mindis == ansdis && aver < ansaver) {
    89. ansid = index;
    90. ansaver = aver;
    91. }
    92. }
    93. if(ansid == -1)
    94. printf("No Solution");
    95. else {
    96. printf("G%d\n", ansid - n);
    97. printf("%.1f %.1f", ansdis, ansaver);//输出一位小数
    98. }
    99. return 0;
    100. }

    L3-011 直捣黄龙

  • 三个条件:最快>城镇最多>有效杀伤最多

  • 存储:最合适路径、最快路数、最短进攻距离、歼敌总数
  • 数据量:城镇数∈[2, 200]
  • 将字符串与数字做映射,方便操作
  • Dijskra
  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. #define ll long long
  4. #define FOR(i, n, m) for (int i = n; i < m; ++i)
  5. struct node
  6. {
  7. int id, dis;
  8. //方便后面的优先队列
  9. bool friend operator<(const node &a, const node &b)
  10. {
  11. return a.dis > b.dis;
  12. }
  13. };
  14. int n, k, s, t, cnt, d,
  15. enemyNum[205], //各个大本营敌军数量
  16. enemyAll[205], //到该点的总共的歼敌数量
  17. pre[205], //到该大本营的上一个点
  18. Dis[205], //起点到各点的最短距离
  19. vis[205],
  20. sum[205], //到该点路线数量
  21. liberation[205]; //到该店解放的大本营数
  22. //映射
  23. map<string, int> ntoi; //name->id
  24. map<int, string> iton; //id->name
  25. vector<pair<int, int>> E[205]; //存图, <u,d>:E[i]到u点,权为d
  26. vector<int> ans;
  27. string S, T, u, v;
  28. void Dijskra()
  29. {
  30. fill(Dis, Dis + 205, 1e9);
  31. Dis[s] = 0;
  32. sum[s] = 1;
  33. priority_queue<node> Q;
  34. Q.push(node{s, 0});
  35. while (!Q.empty())
  36. {
  37. int now = Q.top().id, nowD = Q.top().dis;
  38. Q.pop();
  39. if (vis[now])
  40. continue;
  41. vis[now] = 1;
  42. for (auto it : E[now])
  43. {
  44. int itV = it.first, itD = it.second;
  45. if (Dis[itV] > nowD + itD)
  46. {
  47. Dis[itV] = nowD + itD;
  48. enemyAll[itV] = enemyAll[now] + enemyNum[itV];
  49. sum[itV] = sum[now];
  50. liberation[itV] = liberation[now] + 1;
  51. Q.push(node{itV, Dis[itV]});
  52. pre[itV] = now;
  53. }
  54. else if (Dis[itV] == nowD + itD)
  55. {
  56. sum[itV] += sum[now];
  57. if (liberation[itV] < liberation[now] + 1)
  58. {
  59. liberation[itV] = liberation[now] + 1;
  60. enemyAll[itV] = enemyAll[now] + enemyNum[itV];
  61. Q.push(node{itV, Dis[itV]});
  62. pre[itV] = now;
  63. }
  64. else if (liberation[itV] == liberation[now] + 1)
  65. {
  66. if (enemyAll[itV] < enemyAll[now] + enemyNum[itV])
  67. {
  68. enemyAll[itV] = enemyAll[now] + enemyNum[itV];
  69. Q.push(node{itV, Dis[itV]});
  70. pre[itV] = now;
  71. }
  72. }
  73. }
  74. }
  75. }
  76. }
  77. int main()
  78. {
  79. ios::sync_with_stdio(false);
  80. cin >> n >> k >> S >> T;
  81. for (int i = 0; i < n - 1; i++)
  82. {
  83. cin >> u >> d;
  84. ntoi[u] = ++cnt;
  85. iton[cnt] = u;
  86. enemyNum[cnt] = d;
  87. }
  88. //起点的映射
  89. ntoi[S] = 0, iton[0] = S;
  90. //终点
  91. t = ntoi[T];
  92. for (int i = 0; i < k; i++)
  93. {
  94. cin >> u >> v >> d;
  95. //存边
  96. E[ntoi[u]].push_back({ntoi[v], d});
  97. E[ntoi[v]].push_back({ntoi[u], d});
  98. }
  99. Dijskra();
  100. d = t;
  101. while (d)
  102. {
  103. ans.push_back(d);
  104. d = pre[d];
  105. }
  106. cout << iton[0];
  107. for (int i = ans.size() - 1; i >= 0; i--)
  108. {
  109. cout << "->" << iton[ans[i]];
  110. }
  111. cout << "\n"
  112. << sum[t] << " " << Dis[t] << " " << enemyAll[t] << "\n";
  113. return 0;
  114. }