PAT_甲级_1030 Travel Plan (30分) (C++)【Dijkstra/DFS】

目录

1,题目描述

题目大意

输入

输出

2,思路

数据结构

DFS算法设计

Dijkstra算法设计

3,代码


1,题目描述

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

题目大意

求出图中两点的最短路径。若有多条最短路径,输出花费最小的那条路径;

输入

  1. 第一行:N城市数目(<=500,编号0 - N-1),M路的数目,S起点start,D终点destination;
  2. 接下来M行:表示路的两端连接城市的编号,距离,花费;

输出

  1.  起点到终点的路径, 最短距离, 最少花费

 

2,思路

用Dijkstra算法求出所有最短路径,用DFS遍历所有最短路径,求出最短路径中花费最少的一条(题目保证最短路径中,花费均不相同)。

整体思路类似于这一题PAT_甲级_1018 Public Bike Management (30分) (C++)【Dijkstra保存多条最短路径信息+DFS】

 

数据结构

将部分数据声明为全局变量,可以减少函数对应的参数,但需更加注意变量是否会无意更改。 

  • int graph[500][500], cost[500][500]:graph存放每条路的用时 cost存放每条路的花费;
  • bool visited[500]:记录是否节点已遍历;
  • int dis[500]:记录起点到其余各点的最短距离;
  • vector<int> path[500]:存放所有节点的所有最短路径,eg.,容器path[2]存放了所有可以到达节点2的最短路径的前驱节点
  • vector<int> temPath, finalPath:temPath临时的最终路径 finalPath最终路径;

 

DFS算法设计

  • 递归的顺序是从终点向原点进行(由于path中存放的是前驱节点);
  • 叶节点(递归出口)为起始点; 
  • 可以考虑将花费作为参数进行运算,到达叶节点时再更新最少话费;
  • 注意:为了记录最终的最短路径,开始时需将当前的点入栈,当前节点的所有子树遍历完毕后,弹出该节点;

 

Dijkstra算法设计

基本上都是这个思路,很实用,建议熟记。 

  1. visited数组初始化全为false、dis数组初始化全为INT_MAX、graph中两节点未连接部分初始化全为INT_MAX、dis[s](s即算法的起点)初始化为0;
  2. 寻找dis数组中最小的值对应的节点u(当前可达未遍历节点的最短距离);
  3. 遍历所有与u相邻且未访问过的节点v,若dis[v] > dis[u] + graph[u][v],则更新dis[v],并清除此节点v原先记录的所有最短路径(path[v].clear();),并将新的最短前驱节点存入其中(path[v].push_back(u));若dis[v] == dis[u] + graph[u][v],path[v].push_back(u)即可;

 

3,代码

#include<iostream>
#include<vector>
#include<climits>
#include<algorithm>
using namespace std;

bool visited[500];
int graph[500][500], cost[500][500];                        //graph存放每条路的用时 cost存放每条路的花费
int dis[500];
vector<int> path[500];                                      //存放所有节点的所有最短路径
vector<int> temPath, finalPath;                             //temPath临时的最终路径 finalPath最终路径
int n, m, s, d;                                             //n城市数目 m路数目 s起点 d终点
int minCost = INT_MAX;

void dfs(int end, int co){
    temPath.push_back(end);
    if(end == s){                                           //已经从终点到达起点(倒着进行DFS)
        if(co < minCost){                                   //更新最少花费 以及最终路径
            finalPath = temPath;
            minCost = co;
        }
    }else{
        for(int i = 0; i < path[end].size(); i++){
            int e = path[end][i];
            dfs(e, co + cost[end][e]);
        }
    }
    temPath.pop_back();                                     //记得弹出已完全遍历过的节点
}

int main(){
//#ifdef ONLINE_JUDGE
//#else
//    freopen("1.txt", "r", stdin);
//#endif

    int a, b;
    cin>>n>>m>>s>>d;

    if(s == d){                                             //起点即为终点
        cout<<s<<' '<<0<<' '<<0;
        return 0;
    }

    fill(graph[0], graph[0] + 500 * 500, INT_MAX);
    fill(dis, dis + 500, INT_MAX);
    int dis_, cost_;
    for(int i = 0; i < m; i++){
        scanf("%d%d%d%d", &a, &b, &dis_, &cost_);
        graph[a][b] = graph[b][a] = dis_;
        cost[a][b] = cost[b][a] = cost_;                    //注意是无向图cost[a][b] = cost[b][a]
    }

    dis[s] = 0;                                             //起点先设置dis为0
    for(int i = 0; i < n; i++){
        int minDis = INT_MAX, u = -1;
        for(int j = 0; j < n; j++){
            if(visited[j] == false && dis[j] < minDis){
                u = j;
                minDis = dis[j];
            }
        }

        if(u == -1) break;
        visited[u] = true;                                  //赋值号别写成==

        for(int v = 0; v < n; v++){
            if(visited[v] == false && graph[u][v] < INT_MAX){
                if(dis[v] > dis[u] + graph[u][v]){
                    dis[v] = dis[u] + graph[u][v];
                    path[v].clear();
                    path[v].push_back(u);
                }else if(dis[v] == dis[u] + graph[u][v]){   //保存多条最短路径
                    path[v].push_back(u);
                }
            }
        }
    }

    dfs(d, 0);
    for(int i = finalPath.size() - 1; i >= 0; i--){
        printf("%d ", finalPath[i]);
    }
    printf("%d %d", dis[d], minCost);


    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值