【PAT算法之路】 -- 最短路径 1030 Travel Plan (30 分) C++解法

【PAT算法之路】 – 专栏总揽

最短路径几乎是PAT出现频率最高的题型之一,一般都需要70-100行代码(C++),但是不用慌,因为这个代码有40行以上都是靠背下来的(都是套路)。
我个人记的是Dijkstra+DFS,这种方法感觉比较通用,而且容易记住。

我们用一个例子来看看吧,就比如: 1030 Travel Plan (30 分)

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

题目意思很简单就是从一个指定的点出发,到指定的终点,求最短路径,如果最短路径不止一条,选择Cost最小的那一条。

容我先给出代码:

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

#define INF 99999999
int e[510][510];
int e2[510][510];
int dis[510];
bool vis[510];
vector<int> pre[510];

int n,m,s,d;
int min_cost = INF;
vector<int> path;

void dfs(int c,vector<int> temp_path){
    temp_path.push_back(c);
    if(c == s){
        int sum_cost = 0;
        for(int i=1;i<temp_path.size();i++)
            sum_cost += e2[temp_path[i]][temp_path[i-1]];
        if(min_cost > sum_cost){
            min_cost = sum_cost;
            path = temp_path;
        }
        return;
    }
    int len = pre[c].size();
    for(int i=0;i<len;i++){
        dfs(pre[c][i],temp_path);
    }
    temp_path.pop_back();
}

int main() {
    fill(e[0],e[0]+510*510,INF);
    fill(e2[0],e2[0]+510*510,INF);
    fill(dis,dis+510,INF);

    cin >> n >> m >> s >> d;
    for(int i=0;i<m;i++){
        int c1,c2,diss,cost;
        cin >> c1 >> c2 >> diss >> cost;
        e[c1][c2] = diss;
        e[c2][c1] = diss;
        e2[c1][c2] = cost;
        e2[c2][c1] = cost;
    }

    dis[s] = 0;
    for(int i=0;i<n;i++){
        int min_ = INF,min_j = -1;
        for(int j=0;j<n;j++)
            if(!vis[j] && min_ > dis[j]){
                min_ = dis[j];
                min_j = j;
            }

        if(min_j == -1) break;
        vis[min_j] = true;

        for(int j=0;j<n;j++){
            if(dis[j] > e[min_j][j]+dis[min_j]){
                dis[j] = e[min_j][j]+dis[min_j];
                pre[j].clear();
                pre[j].push_back(min_j);
            }
            else if(dis[j] == e[min_j][j]+dis[min_j])
                pre[j].push_back(min_j);
        }

    }

//    for(auto item:pre[d])
//        cout << item << "\t";

    vector<int> temp_vec;
    dfs(d,temp_vec);

    reverse(path.begin(),path.end());
    for(auto item:path)
        cout << item << " ";

    cout << dis[d] << " ";
    cout << min_cost;
    return 0;
}

一、定义和初始化

首先对于最短路径的题,选择e[][]来存图。大小就是题目给的最大结点数+一点点。

然后就需要定义下面这些的变量

int dis[510];				// 存储起点到每个点的最短距离
bool vis[510];			// 存储是否访问某个节点

vector<int> pre[510];		// 存储前置节点
// (如果只有Dijkstra,用不上这个,这个是连接Dijkstra和DFS的桥梁)

首先要初始化vis[]全为false,还未访问(定义的时候已经自动初始化了),还有就是dis[]、e[][]全为INF。

还有就是正确的读入数据,保存到e[][]中了,有些题有点权,那么就需要定义一个weight[]保存点的权重。

30行代码写出来了。。。/w\

二、Dijkstra算法

这个在理解的基础上直接背:

dis[起点]为0

循环n(结点数)次, 在循环体中选择未访问的,最小dis[]的结点

根据选择的结点更新各个结点的dis[]

如果需要保存前置结点,则在更新dis[]时,更新pre

具体看上面的代码。

再加上20行代码。。。

二.五、检查pre

这个很重要,因为pre马上要用于DFS了,我们可以在写DFS前检查下,方法很简单

//    for(auto item:pre[d])
//        cout << item << "\t"

不出意外会打印结点d的前置结点。如果出了意外,回去仔细检查Dijkstra有没有那里写错了。

三、DFS算法

如果只求点到点的最短距离,那么是不需要DFS,出现DFS一般就是点到点的最短距离不unique,需要根据点权或者像上面这道题的边的其他权重来确定唯一的路径。(都是套路)

但是DFS还是需要大家对递归和回溯有比较好的掌握的,比如记得pop_back什么的。比较统一的写法是,递归结束条件一般都是回到了起点。在这个时候根据递归参数保存的值来更新结果。

具体看上面的代码。

四、打印结果

这个就自己写了。。。

总结,作者水平有限,有哪里有问题都欢迎评论指出。如果对你有帮助欢迎点赞。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值