【PAT】最短路径算法Dijkstra模板总结

模板

#include <iostream>
#define MAX 9999999 // 表示边不存在,用INT32_MAX也可

using namespace std;
int main() {
    int N, M; // 顶点数,边数
    int C1, C2; // 起点,终点
    int A[N][N]; // 记录路径长度

	// 构造邻接矩阵
    cin >> N >> M >> C1 >> C2;
    fill_n(&A[0][0], N * N, MAX); // MAX表示不可达

    for (int i = 0; i < N; i++) cin >> V[i];

    int j, k; // 无向邻接图对称!
    for (int i = 0; i < M; i++) {
        cin >> j >> k >> A[j][k];
        A[k][j] = A[j][k];
    }

    // 最短路径算法
    int visit[N]; // 记录是否在点集中
    int D[N]; // 记录C1到点i的最短路径
    int min, v, w;

    // 初始化(直通路径初始化)
    for (int i = 0; i < N; i++) {
        visit[i] = 0; 
        D[i] = A[C1][i]; // 记录直通的路径长
    }

    visit[C1] = 1; // C1加入已访问节点
    D[C1] = 0; // C1到C1路径长为0
    for (int i = 1; i < N; i++) { // N-1轮
        min = MAX;
        v = -1;
        for (w = 0; w < N; w++) {
            if (!visit[w]) {
                if (D[w] < min) {
                    v = w;
                    min = D[w];
                }
            }
        }
        if (v == -1) break;
        visit[v] = 1;
        for (w = 0; w < N; w++) { //加入w节点,处理D[w]
            if (!visit[w] && A[v][w] != MAX) { // 访问未访问且有直通路径的节点w
                if (min + A[v][w] < D[w]) { // 如果当前记录的最短路径大于经过v的路径
                    D[w] = min + A[v][w]; // 更新D[w]
                } 
            }
        }
    }
}

应用

参考《算法笔记》与《算法笔记-上机训练实战指南》,柳婼大神的博客

PAT1003

#include <iostream>
#define MAX 9999999

using namespace std;
int main() {
    int N, M;
    int C1, C2;
    int V[N]; // 记录每个城市的救援队数量
    int A[N][N]; // 记录路径长度

    cin >> N >> M >> C1 >> C2;

    fill_n(&A[0][0], N * N, MAX); // MAX表示不可达

    for (int i = 0; i < N; i++) cin >> V[i];

    int j, k; // 无向邻接图对称!
    for (int i = 0; i < M; i++) {
        cin >> j >> k >> A[j][k];
        A[k][j] = A[j][k];
    }

    // 最短路径算法
    int visit[N]; // 记录是否在点集中
    int D[N]; // 记录C1到点i的最短路径
    int W[N]; // 记录C1到点i救援队伍数量
    int count[N]; // 记录C1到点i最短路径数
    int min, v, w;

    // 初始化(直通路径初始化)
    for (int i = 0; i < N; i++) {
        visit[i] = 0; 
        D[i] = A[C1][i]; // 记录直通的路径长
        if (A[C1][i] != MAX) W[i] = V[C1] + V[i]; // 记录直通连接的路径权值
        else W[i] = 0;
        count[i] = 1; // C1到i肯定有一条路径
    }

    visit[C1] = 1; // C1加入已访问节点
    D[C1] = 0; // C1到C1路径长为0
    W[C1] = V[C1];
    for (int i = 1; i < N; i++) {
        min = MAX;
        v = -1;
        for (w = 0; w < N; w++) {
            if (!visit[w]) {
                if (D[w] < min) {
                    v = w;
                    min = D[w];
                }
            }
        }
        if (v == -1) break;
        visit[v] = 1;
        for (w = 0; w < N; w++) { //加入w节点,处理D[w],W[w],count[w]
            if (!visit[w] && A[v][w] != MAX) { // 访问未访问且有直通路径的节点w
                if (min + A[v][w] < D[w]) { // 如果当前记录的最短路径大于经过v的路径
                    D[w] = min + A[v][w]; // 更新D[w]
                    count[w] = count[v]; // 更新count[w]
                    W[w] = W[v] + V[w]; // 更新W[w]
                } else if (min + A[v][w] == D[w]) { // 如果当前记录的最短路径等于经过v的路径
                    count[w] = count[w] + count[v]; // 累加上经过v到达w的路径数
                    if (W[w] < W[v] + V[w]) W[w] = W[v] + V[w]; // 更新W[w]
                }
            }
        }
    }
    
    printf("%d %d", count[C2], W[C2]);
}

PAT1018

这题和上一题不同之处在于,此题不能使用单纯的dijkstra,要配合dfs。因为这题minNeed与minRemain不满足最优子结构,即无法边进行路径传递边求的结果,只有当全部路径确定后才能去得到最优答案。(有点玄玄的-。-)
dijkstra:求所有最短路径
dfs:在所求的所有的最短路径上遍历得到最优解

#include <iostream>
#include <vector>
#include <algorithm>
#define MAX 999999
#define MAXV 510

using namespace std;

// 初始数据
int C, N, Sp, M; // 车站最大容量, 车站总数,问题车站序号,边数
int c[MAXV]; // 每个车站包含的单车数
int T[MAXV][MAXV]; // 记录边描述的时间
int Si, Sj, edge;
// 最短路径算法 + DFS
int visit[MAXV];
int D[MAXV];
int mini, v, w;
vector<int> pre[MAXV]; // 记录顶点前驱
vector<int> tempPath, path; // 记录临时路径及最优路径
int minNeed = MAX, minRemain = MAX; // 最少需要补和还的单车数量

void Dijkstra() {
	// 初始化
    for (int i = 0; i <= N; i++) {
        D[i] = T[0][i];
        visit[i] = 0;
        if (T[0][i] != MAX) pre[i].push_back(0);  // 前驱初始化
    }

    visit[0] = 1;
    D[0] = 0;
    for (int i = 1; i <= N; i++) {
        mini = MAX;
        v = -1;
        for (int w = 0; w <= N; w++) {
            if (!visit[w]) {
                if (D[w] < mini) {
                    v = w;
                    mini = D[w];
                }
            }
        }
        
        if (v == -1) break;
        visit[v] = 1;

        for (int w = 0; w <= N; w++) {
            if (!visit[w] && T[v][w] != MAX) {
                if (T[v][w] + D[v] < D[w]) { // 更新最短路径的同时更新前驱
                    D[w] = T[v][w] + D[v];
                    pre[w].clear(); // 换路径了,清空
                    pre[w].push_back(v);
                } else if (T[v][w] + D[v] == D[w]) {
                    pre[w].push_back(v); // 更新,但不请空原有路径
                }
            }
        }
    }
}

// 依次处理时间花费相同的最短路径
void DFS(int v) {
    if (v == 0) { // 递归边界
        // 计算一条路径PBMC要补和收回的自行车数
        tempPath.push_back(v); 
        int need = 0, remain = 0;
        for (int i = tempPath.size() - 2; i >= 0; i--) {
            int id = tempPath[i];
            if (c[id] > 0) { // 说明自行车要带走一部分
                remain += c[id];
            } else { // 反之说明自行车要补一部分
                if (remain < abs(c[id])) { // 不够补,则PBMC要补一部分
                    need += abs(c[id]) - remain;
                    remain = 0;
                } else {
                    remain -= abs(c[id]);
                }
            }
        }

        // 比较得出最优解
        // 条件一:PBMC补出的自行车数最少
        // 条件一相等则考虑条件二:PBMC收回的自行车数最少
        // 题目描述是在最短路径的基础上,考虑条件一,输出时若还有多条路径,考虑条件二
        if (need < minNeed) {
            minNeed = need;
            minRemain = remain;
            path = tempPath; 
        } else if (need == minNeed && remain < minRemain){
            minRemain = remain;
            path = tempPath;
        }

        tempPath.pop_back();
        return;
    }

    tempPath.push_back(v);
    for (int i = 0; i < pre[v].size(); i++) {
        DFS(pre[v][i]);
    }
    tempPath.pop_back();
}

int main() {
    cin >> C >> N >> Sp >> M;
    
    for (int i = 1; i <= N; i++) {
        cin >> c[i];
        c[i] -= C / 2; // 减去车站一半单车,方便判断要补还是还
    }

    fill(T[0], T[0] + MAXV * MAXV, MAX);
    
    for (int i = 0; i < M; i++) {
        cin >> Si >> Sj >> edge;
        T[Si][Sj] = edge;
        T[Sj][Si] = edge;
    }

    // 最短路径算法
    Dijkstra();

    // DFS
    DFS(Sp);

    cout << minNeed << " ";
    for (int i = path.size() - 1; i >= 0; i--) {
        if (i != 0) cout << path[i] << "->";
        else cout << path[i] << " ";
    }
    
    cout << minRemain;
    
    return 0; 
}

PAT1030

这题比上面那题简单,只使用Dijkstra即可
关于路径的描述,上面那题因为要记录全部的最短路径,一个顶点可能对应多个前驱,所以pre采用vector数组来描述,而此题可以一边寻找最短路径一边计算cost,更换最短路径,同时记录一条即可,所以采用int数组描述。

#include <iostream>
#include <vector>
#define MAXV 500
#define Inf 999999

using namespace std;

int N, M, Si, Di; // 城市数量,高铁数量,起点,终点
int Distance[MAXV][MAXV], Cost[MAXV][MAXV]; // 记录城市之间距离和花费

int visit[MAXV];
int D[MAXV];
int pre[MAXV];
vector<int> path;
int C[MAXV]; // 记录到达该点处所需要最少的费用
int v, w, mini;

void Dijkstra() {
    for (int i = 0; i < N; i++) {
        visit[i] = 0;
        D[i] = Distance[Si][i];
        if (Distance[Si][i] != Inf) {
            pre[i] = Si;
            C[i] = Cost[Si][i];
        }
    }

    visit[Si] = 1;
    D[Si] = 0;
    for (int i = 1; i < N; i++) {
        mini = Inf;
        v = -1;
        for (int w = 0; w < N; w++) {
            if (!visit[w] && D[w] < mini) {
                v = w;
                mini = D[w];
            }
        }

        if (v == -1) break;
        visit[v] = 1;

        for (int w = 0; w < N; w++) {
            if (!visit[w] && Distance[v][w] != Inf) {
                if (mini + Distance[v][w] < D[w]) {
                    D[w] = mini + Distance[v][w];
                    C[w] = C[v] + Cost[v][w];
                    pre[w] = v;
                } else if (mini + Distance[v][w] == D[w]) {
                    if (C[v] + Cost[v][w] < C[w]) {
                        C[w] = C[v] + Cost[v][w];
                        pre[w] = v;
                    }
                }
            }
        }
    }
}

int main() {
    cin >> N >> M >> Si >> Di;

    fill(Distance[0], Distance[0] + MAXV * MAXV, Inf);
    fill(Cost[0], Cost[0] + MAXV * MAXV, Inf);

    int City1, City2;
    for (int i = 0; i < M; i++) {
        cin >> City1 >> City2 >> Distance[City1][City2] >> Cost[City1][City2];
        Distance[City2][City1] = Distance[City1][City2];
        Cost[City2][City1] = Cost[City1][City2];
    }

    Dijkstra();

    int w = Di;
    while (w != Si) {
        path.push_back(w);
        w = pre[w];
    }
    path.push_back(w);

    for (int i = path.size() - 1; i >= 0; i--) {
        cout << path[i] << " ";
    }

    cout << D[Di] << " " << C[Di];
    
}

PAT1072

条件很多,主要是输入处理有些麻烦,运行测试用例时五舍六入,3.25->3.2,然后发现平台提交后也是正确的,好坑= =

#include <iostream>
#include <string>
#define MAXV 1020
#define Inf 999999

using namespace std;

/*
把不同的煤气站分别作为起点,进行最短路径求解,期间某所房子与该煤气站超过最大距离时排除,
最后进行比较,哪一煤气站距离任意房子的最短路径最大,若有并列情况,选择输出与所有房子平均最短路径最小的,
若仍有并列情况,输出编号更小的。
    */
int N, M, K, Ds; // 房子总数,煤气站候选点总数,连接房子与煤气站之间路的数量,煤气站的最大服务范围
string P1, P2;
int Dist;
int V; // 房子+煤气站
int Distance[MAXV][MAXV]; 

// 进行最短路径
int mini, v, w;
int D[11][MAXV];
int visit[MAXV];
int D_min; // 记录与任意房子的最短距离中最小值
int best = 0; // 最后选出的候选点
double Avg_min[11] = {0.0}; // 记录平均最短路径
double Any_max[11] = {0.0}; // 记录与任意房子的最短距离尽可能远的距离

void Dijkstra(int s) {
    for (int i = 1; i <= V; i++) {
        visit[i] = 0;
        D[s][i] = Distance[s][i];
    }

    visit[s] = 1;
    D[s][s] = 0;
    D_min = Inf;
    for (int i = 1; i < V; i++) {
        mini = Inf;
        v = -1;
        for (int w = 1; w <= V; w++) {
            if (!visit[w]) {
                if (D[s][w] < mini) {
                    v = w;
                    mini = D[s][w];
                }
            }
        }

        if (v == -1) break;
        visit[v] = 1;
        
        if (v > M) {
            if (Ds < D[s][v]) { // 某所房子超过最大距离,该点排除
                D_min = -1;
                break; 
            }
            if (D[s][v] < D_min) D_min = D[s][v];
            Avg_min[s] += 1.0 * D[s][v];
        }
        
        for (int w = 1; w <= V; w++) {
            if (Distance[v][w] != Inf && mini + Distance[v][w] < D[s][w]) {
                D[s][w] = mini + Distance[v][w];
            }
        }
    }

    Avg_min[s] = Avg_min[s] / N;
    Any_max[s] = D_min;
}

int main() {
    cin >> N >> M >> K >> Ds;
    V = N + M;

    fill(Distance[0], Distance[0] + MAXV * MAXV, Inf);

    int id1, id2;
    for (int i = 0; i < K; i++) {
        cin >> P1 >> P2 >> Dist;
        if (P1[0] == 'G') {
            id1 = stoi(P1.substr(1, P1.length() - 1));
        } else {
            id1 = stoi(P1) + M;
        }
        if (P2[0] == 'G') {
            id2 = stoi(P2.substr(1, P2.length() - 1));
        } else {
            id2 = stoi(P2) + M;
        }
        Distance[id1][id2] = Dist;
        Distance[id2][id1] = Dist;
    }

    for (int s = 1; s <= M; s++) {
        Dijkstra(s);
    }

    double temp = 0.0;
    for (int s = 1; s <= M; s++) {
        if (Any_max[s] == -1) continue;
        if (temp < Any_max[s]) {
            temp = Any_max[s];
            best = s;
        } else if (temp == Any_max[s] && Avg_min[s] < Avg_min[best]) {
            best = s;
        }
    }

    if (best == 0) cout << "No Solution";
    else {
        cout << "G" << best << endl;
        printf("%.1f %.1f", Any_max[best], Avg_min[best]);
    }

}

PAT1087

这题就比较简单点,就是要计算的条件太多,Dijkstra和Dijkstra+DFS都可。
总结套路:
PAT考试一般计算最短路径,最短路径数量,点权值,平均点权值,除了第二道需要看到全部路径才可以计算得出之外,这些都可以通过简单累加得出结论(最优子结构,即大问题分解为小问题)√
输入输出需要稍微处理。
path通过pre计算得出。

#include <iostream>
#include <string>
#include <vector>
#define MAXV 200
#define Inf 999999

using namespace std;

// 条件一:最短cost
// 若条件一得出路径不唯一,条件二:最大快乐值
// 若条件二依旧不唯一,条件三:最大平均快乐值

int N, K; // 城市数量,路数量
string City1, City2;
int cost;
string Cmap[MAXV]; // 记录城市名与编号的映射
int Happiness[MAXV]; // 城市代表的快乐值
int Cost[MAXV][MAXV]; // 城市间的路费

// 最短路径算法
int mini, v, w;
int D[MAXV]; 
int H[MAXV]; // 到达该城市时最大快乐值
int Num[MAXV]; // 到达该城市时需要的最少城市数量
int count[MAXV]; // 到达该城市的最短路径数
int visit[MAXV];
int pre[MAXV];

void Dijkstra() {
    fill(Num, Num + MAXV, Inf);
    fill(count, count + MAXV, 1);
    for (int i = 1; i < N; i++) {
        visit[i] = 0;
        D[i] = Cost[0][i];
        if (Cost[0][i] != Inf) {
            H[i] = Happiness[i];
            pre[i] = 0;
            Num[i] = 1;
        }
    }

    D[0] = 0;
    H[0] = 0;
    Num[0] = 0;
    visit[0] = 1;
    for (int i = 1; i < N; i++) {
        mini = Inf;
        v = -1;

        for (int w = 0; w < N; w++) {
            if (!visit[w]) {
                if (D[w] < mini) {
                    mini = D[w];
                    v = w;
                }
            }
        }

        if (v == -1) break;
        visit[v] = 1;

        for (int w = 0; w < N; w++) {
            if (!visit[w] && Cost[v][w] != Inf) {
                if (mini + Cost[v][w] < D[w]) {
                    D[w] = mini + Cost[v][w];
                    H[w] = H[v] + Happiness[w];
                    count[w] = count[v];
                    Num[w] = Num[v] + 1;
                    pre[w] = v;
                } else if (mini + Cost[v][w] == D[w]) {
                    count[w] += count[v];
                    if (H[w] < H[v] + Happiness[w]) {
                        H[w] = H[v] + Happiness[w];
                        Num[w] = Num[v] + 1;
                        pre[w] = v;
                    } else if (H[v] + Happiness[w] == H[w] && Num[v] + 1 < Num[w]) {
                        Num[w] = Num[v] + 1;
                        pre[w] = v;
                    }    
                }
            }
        }
    }
}

int main() {
    cin >> N >> K >> Cmap[0];

    // 建立映射
    fill(Happiness, Happiness + MAXV, Inf);
    Happiness[0] = 0;
    for (int i = 1; i < N; i++) {
        cin >> Cmap[i] >> Happiness[i];
    }

    fill(Cost[0], Cost[0] + MAXV * MAXV, Inf);
    int id1, id2;
    for (int i = 0; i < K; i++) {
        cin >> City1 >> City2 >> cost;
        for (int j = 0; j < N; j++) {
            if (Cmap[j] == City1) id1 = j;
            if (Cmap[j] == City2) id2 = j;
        }
        Cost[id1][id2] = cost;
        Cost[id2][id1] = cost;
    }

    Dijkstra();

    int e;
    for (int i = 1; i < N; i++) {
        if (Cmap[i] == "ROM") {
            e = i;
            break;
        }
    }

    // for (int i = 1; i < N; i++) {
    //     cout << count[i] << " ";
    // }

    vector<string> path;
    int v = e;
    while (v != 0) {
        path.push_back(Cmap[v]);
        v = pre[v];
    }
    path.push_back(Cmap[v]);
    
    printf("%d %d %d %d\n", count[e], D[e], H[e], H[e]/(path.size() - 1));
    for (int i = path.size() - 1; i >= 0; i--) {
        cout << path[i];
        if (i != 0) cout << "->";
    }
}
  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值