模板
#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 << "->";
}
}