我的PAT-ADVANCED代码仓:https://github.com/617076674/PAT-ADVANCED
原题链接:https://pintia.cn/problem-sets/994805342720868352/problems/994805464397627392
题目描述:
题目翻译:
1030 旅行计划
一张旅行地图描述出了城市间高速公路的长度以及每条高速公路的收费。现在你需要写一个程序帮助旅行者决定他/她的起始城市到目的地城市的最短路径。如果最短路径不唯一,你需要输出收费最低的路径,题目保证结果唯一。
输入格式:
每个输入文件包含一个测试用例。每个测试用例的第一行包含4个正整数N、M、S和D,其中N(<= 500)表示城市数量(城市编号是0 ~ N - 1);M表示高速公路的条数;S和D分别是起始城市和目的地城市。接下来的M行每一行都按下述格式提供一条高速公路的信息:
City1 City2 Distance Cost
其中的数字都是不大于500的整数,且都由空格分隔。
输出格式:
对每一测试用例,在一行中输出从起始城市到目的地城市的最短路径,然后输出最短路径长度和总花费。一行中的所有数字都必须被一个空格分隔,行末不允许有多余的空格。
输入样例:
4 5 0 3
0 1 1 20
1 3 2 30
0 3 4 10
0 2 2 20
2 3 1 20
输出样例:
0 2 3 3 40
知识点:Dijkstra算法、深度优先遍历、Bellman-Ford算法、SPFA算法
思路一:Dijkstra算法(邻接矩阵实现)
时间复杂度和空间复杂度均为O(N ^ 2)。
C++代码:
#include<iostream>
#include<stack>
using namespace std;
int n; //城市处理
int m; //高速公路数量
int s; //起始城市
int d; //目的地城市
int INF = 1000000000; //定义无穷大数
int graph[501][501]; //无向图
int price[501][501] = {0}; //存储高速公路价格信息
int visited[501] = {false}; //标记节点是否已经被访问的数组
int pre[501]; //存储前一个节点信息
int distances[501]; //distances[i]存储从起点s到达节点i的最短路径长度
int costs[501]; //costs[i]存储从起点s到达节点i的最少花费
void dijkstra(int s);
int main() {
cin >> n >> m >> s >> d;
for(int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) {
graph[i][j] = INF;
}
}
int city1, city2, distance, cost;
for(int i = 0; i < m; i++) {
cin >> city1 >> city2 >> distance >> cost;
graph[city1][city2] = distance;
graph[city2][city1] = distance;
price[city1][city2] = cost;
price[city2][city1] = cost;
}
dijkstra(s);
stack<int> showPathStack;
int tempD = d;
showPathStack.push(tempD);
while(tempD != s) {
showPathStack.push(pre[tempD]);
tempD = pre[tempD];
}
while(!showPathStack.empty()) {
cout << showPathStack.top() << " ";
showPathStack.pop();
}
cout << distances[d] << " " << costs[d] << endl;
return 0;
}
void dijkstra(int s) {
for(int i = 0; i < n; i++){
distances[i] = INF;
costs[i] = INF;
}
costs[s] = 0;
distances[s] = 0;
for(int i = 0; i < n; i++){
int u = -1, min = INF;
for(int j = 0; j < n; j++){
if(distances[j] < min && !visited[j]){
u = j;
min = distances[j];
}
}
if(u == -1){
return;
}
visited[u] = true;
for(int v = 0; v < n; v++){
if(!visited[v] && graph[u][v] != INF){
if(distances[u] + graph[u][v] < distances[v]){
distances[v] = distances[u] + graph[u][v];
pre[v] = u;
costs[v] = price[u][v] + costs[u];
}else if(distances[u] + graph[u][v] == distances[v]){
if(price[u][v] + costs[u] < costs[v]){
costs[v] = price[u][v] + costs[u];
pre[v] = u;
}
}
}
}
}
}
C++解题报告:
思路二:Dijkstra算法+深度优先遍历(邻接矩阵实现)
时间复杂度和空间复杂度均为O(N ^ 2)。
C++代码:
#include<iostream>
#include<vector>
using namespace std;
int n; //城市数量
int m; //高速公路数量
int s; //起始城市
int d; //目的地城市
int INF = 1000000000; //无穷大数
int graph[501][501]; //无向图
int price[501][501] = {0}; //存储高速公路价格信息
int visited[501] = {false}; //标记数组
vector<int> pre[501]; //前一节点列表
int distances[501]; //distances[i]代表从起点s到达点i的最短路径长度
vector<int> path; //最优路径
vector<int> tempPath; //临时路径
int optValue = INF; //一开始将最优费用设为无穷大
void dijkstra(int s);
void dfs(int nowVisit);
int main() {
cin >> n >> m >> s >> d;
for(int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) {
graph[i][j] = INF;
}
}
int city1, city2, distance, cost;
for(int i = 0; i < m; i++) {
cin >> city1 >> city2 >> distance >> cost;
graph[city1][city2] = distance;
graph[city2][city1] = distance;
price[city1][city2] = cost;
price[city2][city1] = cost;
}
dijkstra(s);
dfs(d);
for(int i = path.size() - 1; i >= 0; i--){
cout << path[i] << " ";
}
cout << distances[d] << " " << optValue << endl;
return 0;
}
void dijkstra(int s) {
for(int i = 0; i < n; i++){
distances[i] = INF;
}
distances[s] = 0;
for(int i = 0; i < n; i++){
int u = -1, min = INF;
for(int j = 0; j < n; j++){
if(!visited[j] && distances[j] < min){
min = distances[j];
u = j;
}
}
if(u == -1){
return;
}
visited[u] = true;
for(int v = 0; v < n; v++){
if(!visited[v] && graph[u][v] != INF){
if(distances[u] + graph[u][v] < distances[v]){
distances[v] = distances[u] + graph[u][v];
pre[v].clear();
pre[v].push_back(u);
}else if(distances[u] + graph[u][v] == distances[v]){
pre[v].push_back(u);
}
}
}
}
}
void dfs(int nowVisit){
if(nowVisit == s){
tempPath.push_back(nowVisit);
int value = 0;
for(int i = tempPath.size() - 1; i > 0; i--){
value += price[tempPath[i]][tempPath[i - 1]];
}
if(value < optValue){
optValue = value;
path = tempPath;
}
tempPath.pop_back(); //将刚加入的节点删除
return;
}
tempPath.push_back(nowVisit);
for(int i = 0; i < pre[nowVisit].size(); i++){
dfs(pre[nowVisit][i]);
}
tempPath.pop_back();
}
C++解题报告:
思路三:Bellman-Ford算法+深度优先遍历(邻接表实现)
时间复杂度是O(N * M)。空间复杂度是O(N ^ 2)。
C++代码:
#include<iostream>
#include<vector>
#include<set>
using namespace std;
struct node {
int v; //邻接边的目标顶点
int dis; //邻接边的边权
node(int _v, int _dis) : v(_v), dis(_dis) {}; //构造函数
};
int n; //城市数量
int m; //高速公路数量
int s; //起始城市
int d; //目的地城市
int INF = 1000000000; //定义无穷大数
vector<node> graph[501]; //无向图
int price[501][501]; //记录两城市间的价格
set<int> pre[501];
vector<int> path;
vector<int> tempPath;
int optValue = INF;
int distances[501];
bool bellmanFord(int s);
void dfs(int nowVisit);
int main() {
cin >> n >> m >> s >> d;
int city1, city2, distance, cost;
for(int i = 0; i < m; i++) {
cin >> city1 >> city2 >> distance >> cost;
graph[city1].push_back(node(city2, distance));
graph[city2].push_back(node(city1, distance));
price[city1][city2] = cost;
price[city2][city1] = cost;
}
bellmanFord(s);
dfs(d);
for(int i = path.size() - 1; i >= 0; i--) {
cout << path[i] << " ";
}
cout << distances[d] << " " << optValue << endl;
return 0;
}
bool bellmanFord(int s) {
for(int i = 0; i < n; i++) {
distances[i] = INF;
}
distances[s] = 0;
for(int i = 0; i < n - 1; i++) { //执行n - 1轮操作,n为定点数
for(int u = 0; u < n; u++) { //每轮操作都遍历所有顶点
for(int j = 0; j < graph[u].size(); j++) {
int v = graph[u][j].v;
int dis = graph[u][j].dis;
if(distances[u] + dis < distances[v]) {
distances[v] = distances[u] + dis; //松弛操作
pre[v].clear();
pre[v].insert(u);
} else if(distances[u] + dis == distances[v]) {
pre[v].insert(u);
}
}
}
}
for(int u = 0; u < n; u++) { //每轮操作都遍历所有顶点
for(int j = 0; j < graph[u].size(); j++) {
int v = graph[u][j].v;
int dis = graph[u][j].dis;
if(distances[u] + dis < distances[v]) {
return false;
}
}
}
return true;
}
void dfs(int nowVisit) {
if(nowVisit == s) {
tempPath.push_back(nowVisit);
int value = 0;
for(int i = tempPath.size() - 1; i > 0; i--) {
value += price[tempPath[i]][tempPath[i - 1]];
}
if(value < optValue) {
path = tempPath;
optValue = value;
}
tempPath.pop_back();
return;
}
tempPath.push_back(nowVisit);
set<int>::iterator it;
for(it = pre[nowVisit].begin(); it != pre[nowVisit].end(); it++) {
dfs(*it);
}
tempPath.pop_back();
}
C++解题报告:
思路四:SPFA算法+深度优先遍历(邻接表实现)
期望时间复杂度是O(kM),其中k是一个常数,在很多情况下k不超过2,可见这个算法异常高效,并且经常性地优于堆优化的Dijkstra算法。
C++代码:
#include<iostream>
#include<vector>
#include<queue>
#include<set>
using namespace std;
struct node{
int v; //邻接边的顶点编号
int dis; //邻接边的边权
node(int _v, int _dis) : v(_v), dis(_dis) {} //构造函数
};
int n; //城市数量
int m; //高速公路数量
int s; //起始城市
int d; //目的地城市
int INF = 1000000000; //无穷大数
vector<node> graph[501]; //无向图
int price[501][501]; //记录两地高速公路间的价格
set<int> pre[501]; //前一个节点set集合
bool inq[501] = {false}; //记录节点是否在队列中
int num[501] = {0}; //记录每个节点的入队次数
int distances[501]; //记录最短路径长度
vector<int> path; //记录最优路径
vector<int> tempPath; //记录当前路径
int optValue = INF;
bool spfa(int s);
void dfs(int nowVisit);
int main(){
cin >> n >> m >> s >> d;
int city1, city2, distance, cost;
for(int i = 0; i < m; i++){
cin >> city1 >> city2 >> distance >> cost;
graph[city1].push_back(node(city2, distance));
graph[city2].push_back(node(city1, distance));
price[city1][city2] = cost;
price[city2][city1] = cost;
}
spfa(s);
dfs(d);
for(int i = path.size() - 1; i >= 0; i--){
cout << path[i] << " ";
}
cout << distances[d] << " " << optValue << endl;
return 0;
}
bool spfa(int s){
for(int i = 0; i < n; i++){
distances[i] = INF;
}
distances[s] = 0;
queue<int> q;
q.push(s);
inq[s] = true;
num[s]++;
while(!q.empty()){
int u = q.front();
q.pop();
inq[u] = false;
for(int i = 0; i < graph[u].size(); i++){
int v = graph[u][i].v;
int dis = graph[u][i].dis;
if(dis + distances[u] < distances[v]){
distances[v] = dis + distances[u];
pre[v].clear();
pre[v].insert(u);
if(!inq[v]){
q.push(v);
inq[v] = true;
num[v]++;
if(num[v] > n - 1){
return false;
}
}
}else if(dis + distances[u] == distances[v]){
pre[v].insert(u);
if(!inq[v]){
q.push(v);
inq[v] = true;
num[v]++;
if(num[v] > n - 1){
return false;
}
}
}
}
}
return true;
}
void dfs(int nowVisit){
if(nowVisit == s){
tempPath.push_back(nowVisit);
int value = 0;
for(int i = tempPath.size() - 1; i > 0; i--){
value += price[tempPath[i]][tempPath[i - 1]];
}
if(value < optValue){
optValue = value;
path = tempPath;
}
tempPath.pop_back();
return;
}
tempPath.push_back(nowVisit);
set<int>::iterator it;
for(it = pre[nowVisit].begin(); it != pre[nowVisit].end(); it++){
dfs(*it);
}
tempPath.pop_back();
}
C++解题报告:
思路五:Dijkstra算法+深度优先遍历(邻接表实现)
时间复杂度是O(N * M)。空间复杂度是O(N + M)。
C++代码:
#include<iostream>
#include<vector>
using namespace std;
struct node {
int v;
int distance;
int cost;
node(int _v, int _distance, int _cost) : v(_v), distance(_distance), cost(_cost) {};
};
int N;
int M;
int S;
int D;
int INF = 1000000000;
vector<node> graph[500];
bool visited[500] = {false};
int d[500];
vector<int> pre[500];
vector<int> path;
vector<int> tempPath;
int minCost = INF;
void dijkstra(int s);
void dfs(int nowVisit);
int main(){
cin >> N >> M >> S >> D;
int city1, city2, distance, cost;
for(int i = 0; i < M; i++){
cin >> city1 >> city2 >> distance >> cost;
graph[city1].push_back(node(city2, distance, cost));
graph[city2].push_back(node(city1, distance, cost));
}
dijkstra(S);
dfs(D);
for(int i = path.size() - 1; i >= 0; i--){
cout << path[i] << " ";
}
cout << d[D] << " " << minCost << endl;
return 0;
}
void dijkstra(int s){
for(int i = 0; i < N; i++){
d[i] = INF;
}
d[s] = 0;
for(int i = 0; i < N; i++){
int u = -1, min = INF;
for(int j = 0; j < N; j++){
if(!visited[j] && d[j] < min){
min = d[j];
u = j;
}
}
if(u == -1){
return;
}
visited[u] = true;
for(int j = 0; j < graph[u].size(); j++){
int v = graph[u][j].v;
int distance = graph[u][j].distance;
if(!visited[v]){
if(d[u] + distance < d[v]){
d[v] = d[u] + distance;
pre[v].clear();
pre[v].push_back(u);
}else if(d[u] + distance == d[v]){
pre[v].push_back(u);
}
}
}
}
}
void dfs(int nowVisit){
tempPath.push_back(nowVisit);
if(nowVisit == S){
int value = 0;
for(int i = tempPath.size() - 1; i > 0; i--){
for(int j = 0; j < graph[tempPath[i]].size(); j++){ //用邻接表实现求边权时需要遍历其所有邻边
if(graph[tempPath[i]][j].v == tempPath[i - 1]){
value += graph[tempPath[i]][j].cost;
break;
}
}
}
if(value < minCost){
minCost = value;
path = tempPath;
}
tempPath.pop_back();
return;
}
for(int i = 0; i < pre[nowVisit].size(); i++){
dfs(pre[nowVisit][i]);
}
tempPath.pop_back();
}
C++解题报告:
思路六:Bellman-Ford算法+深度优先遍历(邻接矩阵实现)
时间复杂度是O(N ^ 3)。空间复杂度是O(N ^ 2)。
C++代码:
#include<iostream>
#include<vector>
#include<set>
using namespace std;
int N;
int M;
int S;
int D;
int INF = 1000000000;
int graph[500][500];
int price[500][500];
int d[500];
set<int> pre[500];
vector<int> path;
vector<int> tempPath;
int minCost = INF;
bool bellmanFord(int s);
void dfs(int nowVisit);
int main() {
cin >> N >> M >> S >> D;
int city1, city2, distance, cost;
for(int i = 0; i < N; i++) {
for(int j = 0; j < N; j++) {
graph[i][j] = graph[j][i] = INF;
}
}
for(int i = 0; i < M; i++) {
cin >> city1 >> city2 >> distance >> cost;
graph[city1][city2] = graph[city2][city1] = distance;
price[city1][city2] = price[city2][city1] = cost;
}
bellmanFord(S);
dfs(D);
for(int i = path.size() - 1; i >= 0; i--) {
cout << path[i] << " ";
}
cout << d[D] << " " << minCost << endl;
return 0;
}
bool bellmanFord(int s) {
for(int i = 0; i < N; i++) {
d[i] = INF;
}
d[s] = 0;
for(int i = 0; i < N - 1; i++) {
for(int u = 0; u < N; u++) {
for(int v = 0; v < N; v++) {
int distance = graph[u][v];
if(distance != INF) {
if(d[u] + distance < d[v]) {
d[v] = d[u] + distance;
pre[v].clear();
pre[v].insert(u);
} else if(d[u] + distance == d[v]) {
pre[v].insert(u);
}
}
}
}
}
for(int u = 0; u < N; u++) {
for(int v = 0; v < N; v++) {
int distance = graph[u][v];
if(distance != INF) {
if(d[u] + distance < d[v]) {
return false;
}
}
}
}
return true;
}
void dfs(int nowVisit) {
tempPath.push_back(nowVisit);
if(nowVisit == S) {
int value = 0;
for(int i = tempPath.size() - 1; i > 0; i--) {
value += price[tempPath[i]][tempPath[i - 1]];
}
if(value < minCost) {
minCost = value;
path = tempPath;
}
tempPath.pop_back();
return;
}
set<int>::iterator it;
for(it = pre[nowVisit].begin(); it != pre[nowVisit].end(); it++){
dfs(*it);
}
tempPath.pop_back();
}
C++解题报告:
思路七:SPFA算法+深度优先遍历(邻接矩阵实现)
期望时间复杂度是O(kM),其中k是一个常数,在很多情况下k不超过2,可见这个算法异常高效,并且经常性地优于堆优化的Dijkstra算法。
C++代码:
#include<iostream>
#include<vector>
#include<set>
#include<queue>
using namespace std;
int N;
int M;
int S;
int D;
int INF = 1000000000;
int graph[500][500];
int price[500][500];
int d[500];
set<int> pre[500];
vector<int> path;
vector<int> tempPath;
int minCost = INF;
bool inq[500] = {false};
int countInq[500] = {0};
bool spfa(int s);
void dfs(int nowVisit);
int main() {
cin >> N >> M >> S >> D;
int city1, city2, distance, cost;
for(int i = 0; i < N; i++) {
for(int j = 0; j < N; j++) {
graph[i][j] = graph[j][i] = INF;
}
}
for(int i = 0; i < M; i++) {
cin >> city1 >> city2 >> distance >> cost;
graph[city1][city2] = graph[city2][city1] = distance;
price[city1][city2] = price[city2][city1] = cost;
}
spfa(S);
dfs(D);
for(int i = path.size() - 1; i >= 0; i--) {
cout << path[i] << " ";
}
cout << d[D] << " " << minCost << endl;
return 0;
}
bool spfa(int s) {
for(int i = 0; i < N; i++) {
d[i] = INF;
}
d[s] = 0;
queue<int> q;
q.push(s);
inq[s] = true;
countInq[s]++;
while(!q.empty()) {
int u = q.front();
q.pop();
inq[u] = false;
for(int v = 0; v < N; v++) {
int distance = graph[u][v];
if(distance != INF) {
if(d[u] + distance < d[v]) {
d[v] = d[u] + distance;
pre[v].clear();
pre[v].insert(u);
if(!inq[v]){
q.push(v);
inq[v] = true;
countInq[v]++;
if(countInq[v] >= N){
return false;
}
}
} else if(d[u] + distance == d[v]) {
pre[v].insert(u);
if(!inq[v]){
q.push(v);
inq[v] = true;
countInq[v]++;
if(countInq[v] >= N){
return false;
}
}
}
}
}
}
return true;
}
void dfs(int nowVisit) {
tempPath.push_back(nowVisit);
if(nowVisit == S) {
int value = 0;
for(int i = tempPath.size() - 1; i > 0; i--) {
value += price[tempPath[i]][tempPath[i - 1]];
}
if(value < minCost) {
minCost = value;
path = tempPath;
}
tempPath.pop_back();
return;
}
set<int>::iterator it;
for(it = pre[nowVisit].begin(); it != pre[nowVisit].end(); it++) {
dfs(*it);
}
tempPath.pop_back();
}
C++解题报告: