【PAT甲级】1030 Travel Plan (30分):最短路径Dijkstra/Dijkstra+DFS

1030 Travel Plan (30分)

A traveler's map gives the distances between cities along the highways, together with the cost of each highway. Now you are supposed to write a program to help a traveler to decide the shortest path between his/her starting city and the destination. If such a shortest path is not unique, you are supposed to output the one with the minimum cost, which is guaranteed to be unique.

Input Specification:

Each input file contains one test case. Each case starts with a line containing 4 positive integers N, M, S, and D, where N (≤500) is the number of cities (and hence the cities are numbered from 0 to N−1); M is the number of highways; S and D are the starting and the destination cities, respectively. Then M lines follow, each provides the information of a highway, in the format:

City1 City2 Distance Cost

      
    

where the numbers are all integers no more than 500, and are separated by a space.

Output Specification:

For each test case, print in one line the cities along the shortest path from the starting point to the destination, followed by the total distance and the total cost of the path. The numbers must be separated by a space and there must be no extra space at the end of output.

Sample Input:

4 5 0 3
0 1 1 20
1 3 2 30
0 3 4 10
0 2 2 20
2 3 1 20

      
    

Sample Output:

0 2 3 3 40

分析:最短路径Dijkstra/Dijkstra+DFS(参考《算法笔记》p386-p391)

求:最短路径,若最短路径不唯一则选择最小花费的那条

输出:起点 途径城市 终点 最短路径长度 最短路径花费

  • 城市编号:0到N-1
  • 无向图

注意

  • 有关图的题目,有时题意可能理解不清,部分AC的时候,考虑是不是无向图/有向图。

这题我本来以为是有向图,因为题目里提到了输入是起点城市到终点城市,结果是无向图。。

  • 最短路径 思路:

    1491599-20200207120747919-1381454688.png

代码

  • 第一种:主Dijkstra算法(对于这题第一种更方便)

    Dijkstra算法找最短且最小花费路径,DFS递归输出最短路径的途径节点

/*Dijkstra算法*/

#include<iostream>
#include<vector>

using namespace std;

const int maxN=510;
const int INF=1000000000;
struct Node {
    int cityNo,dis,cost;//Adj[u][j].dis 表示u到j的边权,cost表示该边的花费
};
vector<Node> Adj[maxN];//邻接表实现 有向权图
int pre[maxN];//pre[u] 最短路径上u的前驱结点
int distan[maxN];//distan[u] S到u的最短距离
int Cost[maxN];//Cost[u] S到u的最少花费
bool vis[maxN];
int N,M,S,D;

void Dijkstra() {
    fill(vis,vis+maxN,false);
    fill(distan,distan+maxN,INF);
    fill(Cost,Cost+maxN,INF);
//  for(int i=0; i<N; i++) pre[i]=i; //为什么pre需要赋初始值???不赋值可以吗
    distan[S]=0;
    Cost[S]=0;

    for(int k=0; k<N; k++) {
        //找未访问过且最小的distan[u]
        int u=-1;
        int minDistan=INF;
        for(int i=0; i<N; i++) {
            if(vis[i]==false&&distan[i]<minDistan) {
                u=i;
                minDistan=distan[i];
            }
        }

        if(u==-1) return;//S没有临界点,结束

        vis[u]=true;
        struct Node v;
        for(int i=0; i<Adj[u].size(); i++) { //遍历所有与u邻接的点
            v=Adj[u][i];
            int vNo=v.cityNo;
            if(vis[vNo]==false) {
                if(distan[u]+v.dis<distan[vNo]) {//最优解
                    distan[vNo]=distan[u]+v.dis;
                    Cost[vNo]=Cost[u]+v.cost;
                    pre[vNo]=u;
                } else if(distan[u]+v.dis==distan[vNo]) { //多条最短路径
                    if(Cost[u]+v.cost<Cost[vNo]) { //选择花费更少的
                        Cost[vNo]=Cost[u]+v.cost;
                        pre[vNo]=u;
                    }
                }
            }
        }
    }

}

//递归输出最短路径的结点
void DFS(int now) { //now为当前访问的顶点(从终点开始递归)
    if(now==S) {   //到达终点S
        printf("%d ",S);
        return;
    }

    DFS(pre[now]);//递归访问now的前驱结点
    printf("%d ",now);//从最深层return回来后,输出每一层的顶点编号
}

int main() {
    scanf("%d%d%d%d",&N,&M,&S,&D);
    int s,d,dis,ct;
    for(int i=0; i<M; i++) {
        scanf("%d%d%d%d",&s,&d,&dis,&ct);
        Adj[s].push_back({d,dis,ct});
        Adj[d].push_back({s,dis,ct});
    }

    Dijkstra();
    DFS(D);
    printf("%d %d",distan[D],Cost[D]);

    return 0;
}

  • 第二种:Dijkstra算法+DFS

    Dijkstra算法找最短路径,DFS找最短路径中最小花费的路径并保存途径节点

/*Dijkstra算法+DFS*/

#include<iostream>
#include<vector>

using namespace std;

const int maxN=510;
const int INF=1000000000;
struct Node {
    int cityNo,dis;//Adj[u][j].dis 表示u到j的边权
};
vector<Node> Adj[maxN];//邻接表实现 有向权图
int cost[maxN][maxN];//cost[u][v] 点u到点v的花费
vector<int> pre[maxN];//pre[u] 最短路径上u的前驱结点(若有多条最短路径则有多个前驱)
int distan[maxN];//distan[u] S到u的最短距离
bool vis[maxN];
int N,M,S,D;

void Dijkstra() {
    fill(vis,vis+maxN,false);
    fill(distan,distan+maxN,INF);
    distan[S]=0;

    for(int k=0; k<N; k++) {
        //找未访问过且最小的distan[u]
        int u=-1;
        int minDistan=INF;
        for(int i=0; i<N; i++) {
            if(vis[i]==false&&distan[i]<minDistan) {
                u=i;
                minDistan=distan[i];
            }
        }

        if(u==-1) return;//S没有临界点,结束

        vis[u]=true;
        struct Node v;
        for(int i=0; i<Adj[u].size(); i++) { //遍历所有与u邻接的点
            v=Adj[u][i];
            int vNo=v.cityNo;
            if(vis[vNo]==false) {
                if(distan[u]+v.dis<distan[vNo]) {//最优解
                    distan[vNo]=distan[u]+v.dis;
                    pre[vNo].clear();
                    pre[vNo].push_back(u);

                } else if(distan[u]+v.dis==distan[vNo]) { //多条最短路径
                    pre[vNo].push_back(u);
                }
            }
        }
    }
}
vector<int> tempPath,path;//倒序存放最短路径的节点
int minCost=INF;

//选择花费最少的最短路径
void DFS(int v) {
    if(v==S) {
        tempPath.push_back(v);//放入起点,一条最短路径确认完毕
        //计算总花费
        int tempCost=0;
        for(int i=tempPath.size()-1; i>0; i--) {
            tempCost+=cost[tempPath[i]][tempPath[i-1]];
        }
        if(tempCost<minCost) {
            minCost=tempCost;
            path=tempPath;
        }
        tempPath.pop_back();
    }

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

int main() {
    scanf("%d%d%d%d",&N,&M,&S,&D);
    int s,d,dis,ct;
    for(int i=0; i<M; i++) {
        scanf("%d%d%d%d",&s,&d,&dis,&ct);
        Adj[s].push_back({d,dis});
        Adj[d].push_back({s,dis});
        cost[s][d]=ct;
        cost[d][s]=ct;
    }

    Dijkstra();
    DFS(D);
    for(int i=path.size()-1; i>=0; i--) {//倒序输出最短路径
        printf("%d ",path[i]);
    }
    printf("%d %d",distan[D],minCost);


    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值