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 N N, M M M, S S S, and D D D, where N ( ≤ 500 ) N (\le500) N(500) is the number of cities (and hence the cities are numbered from 0 0 0 to N − 1 N-1 N1); M M M is the number of highways; S S S and D D D are the starting and the destination cities, respectively. Then M M 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

题目大意

给出一张地图,地图包含各个城市之间的道路,每条路的距离以及花费,给出旅行者的出发地和目的地,要求计算出旅行者从出发地到目的地的最短路径,输出应包含出发地到目的地的各个城市,最短路径以及最短路径对应的花费

解题思路

整理思路应首先注意题目的核心问题,最短路径有多条,当有多条路径时,要选出花费最少的路径,基于这个问题,可以将问题分解为两个步骤:

  1. 求出最短路径(包含多条);可以采用dijkstra算法,用vector数组记录每个点在最短路径上的前驱节点,当然这样的前驱节点可能有多个(多条最短路径);
  2. 根据求得的最短路径,从目的地到出发地反向dfs,当然这个dfs需要将所有的路径全部遍历一遍,选出花费最小的路径;

解题技巧

  1. 其实这个题用邻接矩阵存储便于实现,但考虑到邻接表适用更广泛,所以我采用邻接表;
  2. dijkstra在记录路径的时候,当更新路径时,要注意将vector数组清零再push()
  3. dfs遍历时,因为要遍历所有的路径,所以每一层递归都要考虑回代的问题;
  4. 路径可以用临时变量stack保存,当遍历到出发点时,再与最终变量stack比较,看是否更新

从的来说,这是一道比较经典的dijkstradfs经典题目,个人人为是一道很好的题目,需要多多练习这种类型的题目。具体的思路在代码中有详细的注释。

代码

#include <iostream>
#include <cstdio>
#include <vector>
#include <stack>
using namespace std;

const int INF = 0x7fffffff;

struct Node{
    int id;
    int weight;
    int cost;
    Node *next;
};

struct Root{
    Node *next;
}root[500];

int n, m, s, d;
int visited[500];
int dist[500];
vector<int> path[500];
stack<int> anspath, temppath;
int anscost = INF, tempcost = 0;

void dijkstra(int s){
    // 第一趟dijkstra,
    visited[s] = 1;
    Node *p = root[s].next;
    while(p){
        dist[p->id] = p->weight;
        path[p->id].push_back(s);
        p = p->next;
    }
    // 每趟dijkstra都会选取一个点,故只需要循环n-1次
    for(int i=0; i<n-1; i++){
        // 选取当前未被访问节点的最小dist值对应的点
        int k, min = INF;
        for(int j=0; j<n; j++){
            if(!visited[j] && dist[j] < min){
                k = j;
                min = dist[j];
            }
        }
        // 标记被访问
        visited[k] = 1;
        p = root[k].next;
        while(p){
            if(!visited[p->id]){
                if(dist[k] + p->weight < dist[p->id]){ // 更新最短路径
                    dist[p->id] = dist[k] + p->weight;
                    path[p->id].clear();
                    path[p->id].push_back(k);
                }
                else if(dist[k] + p->weight == dist[p->id]){ // 保存另一条最短路径
                    path[p->id].push_back(k);
                }
            }
            p = p->next;
        }
    }
}

void dfs(int d){
    // 标记被访问,同时插入temppath中
    visited[d] = 1;
    temppath.push(d);
    for(int i=0; i<path[d].size(); i++){ // 访问当前节点的多个前驱节点
        int t = path[d][i];
        if(visited[t])      // 避免往回走
            continue;
        int c;
        Node *p = root[d].next;
        while(p){          // 记录到达当前点的总的路径长度和花费
            if(p->id == t){
                c = p->cost;
                tempcost += c;
                break;
            }
            p = p->next;
        }
        dfs(t);      // 遍历下一层
        tempcost -= c; // 回代,因为此时再往回走
    }
    if(d == s){     // 更新最终结果
        if(tempcost < anscost){
            anscost = tempcost;
            anspath = temppath;
        }
    }
    visited[d] = 0;
    temppath.pop();
}

int main(){
    scanf("%d%d%d%d",&n, &m, &s, &d);
    for(int i=0; i<n; i++){
        root[i].next = NULL;
    }
    // 建立矩阵存储
    for(int i=0; i<m; i++){
        int x, y, w, c;
        scanf("%d%d%d%d", &x, &y, &w, &c);
        Node *p = new Node;
        Node *q = new Node;
        p->id = y;
        q->id = x;
        p->weight = q->weight = w;
        p->cost = q->cost = c;
        p->next = root[x].next;
        q->next = root[y].next;
        root[x].next = p;
        root[y].next = q;
    }
    // 初始化dist和visited数组
    fill(dist, dist+n, INF);
    fill(visited, visited+n, 0);
    // dijkstra求出所有最短路径
    dijkstra(s);
    // 再次初始化visited数组,以进行dfs
    fill(visited, visited+n, 0);
    // dfs求出最终路径,并且计算了最少花费
    dfs(d);
    // 输出路径
    while(!anspath.empty()){
        printf("%d ", anspath.top());
        anspath.pop();
    }
    printf("%d %d\n", dist[d], anscost);
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值