Sicily 1031. Campus(shortest path)

本文介绍了一种解决校园内两点间最短路径问题的方法,包括Floyd-Warshall算法和Dijkstra算法的不同实现方式,并提供了详细的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1031. Campus

Constraints

Time Limit: 1 secs, Memory Limit: 32 MB

Description

At present, Zhongshan University has 4 campuses with a total area of 6.17 square kilometers sitting respectively on both sides of the Pearl River or facing the South China Sea. The Guangzhou South Campus covers an area of 1.17 square kilometers, the North Campus covers an area of 0.39 square kilometers, the Guangzhou East Campus has an area of 1.13 square kilometers and the Zhuhai Campus covers an area of 3.48 square kilometers. All campuses have exuberance of green trees, abundance of lawns and beautiful sceneries, and are ideal for molding the temperaments, studying and doing research.

 

 

       Sometime, the professors and students have to go from one place to another place in one campus or between campuses. They want to find the shortest path between their source place S and target place T. Can you help them?

 

 

Input

The first line of the input is a positive integer C. C is the number of test cases followed. In each test case, the first line is a positive integer N (0<N<=100) that represents the number of roads. After that, N lines follow. The i-th(1<=i<=N) line contains two strings Si, Ti and one integer Di (0<=Di<=100). It means that there is a road whose length is Di between Si and Ti. Finally, there are two strings S and T, you have to find the shortest path between S and T. S, T, Si(1<=i<=N) and Ti(1<=i<=N) are all given in the following format: str_Campus.str_Place. str_Campus represents the name of the campus, and str_Place represents the place in str_Campus. str_Campus is "North", "South", "East" or "Zhuhai". str_Place is a string which has less than one hundred lowercase characters from "a-z". You can assume that there is at most one road directly between any two places.

Output

The output of the program should consist of C lines, one line for each test case. For each test case, the output is a single line containing one integer. If there is a path between S and T, output the length of the shortest path between them. Otherwise just output "-1" (without quotation mark). No redundant spaces are needed.

Sample Input

de style="color: rgb(51, 51, 51); font-size: 18.01801872253418px; line-height: 27.027027130126953px;" >
1
2
South.xiaolitang South.xiongdelong 2
South.xiongdelong Zhuhai.liyuan 100
South.xiongdelong South.xiaolitang
de>

Sample Output

de style="color: rgb(51, 51, 51); font-size: 18.01801872253418px; line-height: 27.027027130126953px;" >
2
de>

Problem Source

ZSUACM Team Member

极度坑爹的一题,不过也学到了很多,顺便以这道来研究最短路径问题好了,注意地点相同的特殊情况(坑),还有一个要记住的地方就是丫的给你100条路最多可以有200个地方啊- -


首先是没有用map的Floyd-Warshall算法:


#include <stdio.h>
#include <string.h>
#define INF 99999999
#define MAX 115 * 2

int total_num_of_name, map[MAX][MAX];
char num_of_name[MAX][MAX];

int min(int a, int b) {
    return a > b ? b : a;
}

void make_map(char f[], char t[], int dis) {
    int i, t_num, f_num;
    
    for (i = 0; i < total_num_of_name; i++) {
        if (strcmp(f, num_of_name[i]) == 0) {
            f_num = i;
            break;
        }
    }
    if (i >= total_num_of_name) {
        f_num = total_num_of_name;
        strcpy(num_of_name[total_num_of_name++], f);
    }
    
    for (i = 0; i < total_num_of_name; i++) {
        if (strcmp(t, num_of_name[i]) == 0) {
            t_num = i;
            break;
        }
    }
    if (i >= total_num_of_name) {
        t_num = total_num_of_name;
        strcpy(num_of_name[total_num_of_name++], t);
    }
    
    map[f_num][t_num] = dis < map[f_num][t_num] ? dis : map[f_num][t_num];
    map[t_num][f_num] = dis < map[t_num][f_num] ? dis : map[t_num][f_num];
}

int find_num(char a[]) {
    for (int i = 0; i < total_num_of_name; i++) {
        if (strcmp(a, num_of_name[i]) == 0)
            return i;
    }
    return -1;
}

void Floyd_Warshall() {
    int i, j, k;
    for (i = 0; i < MAX; i++) {
        map[i][i] = 0;
    }
    for (k = 0; k < total_num_of_name; k++) {
        for (i = 0; i < total_num_of_name; i++) {
            for (j = 0; j < total_num_of_name; j++) {
                map[i][j] = min(map[i][j], map[i][k] + map[k][j]);
            }
        }
    }
}

int main() {
    int case_num, n, dis, f_num, t_num;
    char temp_f[MAX], temp_t[MAX];
    scanf("%d", &case_num);
    while (case_num--) {
        scanf("%d", &n);
        for (int i = 0; i < MAX; i++) {
            for (int j = 0; j < MAX; j++) {
                map[i][j] = INF;
            }
        }
        memset(num_of_name, '\0', sizeof(num_of_name));
        total_num_of_name = 0;
        while (n--) {
            memset(temp_f, '\0', sizeof(temp_f));
            memset(temp_t, '\0', sizeof(temp_t));
            scanf("%s %s %d", temp_f, temp_t, &dis);
            make_map(temp_f, temp_t, dis);
        }
        scanf("%s%s", &temp_f, &temp_t);
        if (strcmp(temp_f, temp_t) == 0) {
            printf("0\n");
            continue;
        }
        f_num = find_num(temp_f);
        t_num = find_num(temp_t);
        if (f_num == -1 || t_num == -1) {
            printf("-1\n");
            continue;
        }
        Floyd_Warshall();
        printf("%d\n", map[f_num][t_num] >= INF ? -1 : map[f_num][t_num]);

    }
    return 0;
}
然后试试用map:


#include <stdio.h>
#include <string.h>
#include <iostream>
#include <map>
#include <string>
using namespace std;
#define INF 99999999
#define MAX 115 * 2

int total_num_of_name, mapp[MAX][MAX];
char num_of_name[MAX][MAX];

int min(int a, int b) {
    return a > b ? b : a;
}

void Floyd_Warshall() {
    int i, j, k;
    for (i = 0; i < MAX; i++) {
        mapp[i][i] = 0;
    }
    for (k = 0; k < total_num_of_name; k++) {
        for (i = 0; i < total_num_of_name; i++) {
            for (j = 0; j < total_num_of_name; j++) {
                mapp[i][j] = min(mapp[i][j], mapp[i][k] + mapp[k][j]);
            }
        }
    }
}

int main() {
    
    std::ios::sync_with_stdio(false);
    
    int case_num, n, dis;
    string temp_f, temp_t;
    cin >> case_num;
    
    while (case_num--) {
        cin >> n;
        
        for (int i = 0; i < MAX; i++) {
            for (int j = 0; j < MAX; j++) {
                mapp[i][j] = INF;
            }
        }
        total_num_of_name = 0;
        map<string, int> m;
        
        while (n--) {
            cin >> temp_f >> temp_t >> dis;
            if (m.find(temp_f) == m.end()) {
                m[temp_f] = total_num_of_name++;
            }
            if (m.find(temp_t) == m.end()) {
                m[temp_t] = total_num_of_name++;
            }
            mapp[m[temp_f]][m[temp_t]] = dis < mapp[m[temp_f]][m[temp_t]] ? dis : mapp[m[temp_f]][m[temp_t]];
            mapp[m[temp_t]][m[temp_f]] = dis < mapp[m[temp_t]][m[temp_f]] ? dis : mapp[m[temp_t]][m[temp_f]];
        }
        
        cin >> temp_f >> temp_t;
        if (temp_f == temp_t) {
            cout << 0 << endl;
            continue;
        }
        
        if (m.find(temp_f) == m.end() || m.find(temp_t) == m.end()) {
            cout << -1 << endl;
            continue;
        }
        
        Floyd_Warshall();
        
        cout << (mapp[m[temp_f]][m[temp_t]] >= INF ? -1 : mapp[m[temp_f]][m[temp_t]]) << endl;

    }
    return 0;
}
有map的Dijkstra:


#include <stdio.h>
#include <string.h>
#include <iostream>
#include <map>
#include <string>
using namespace std;
#define INF 99999999
#define MAX 115 * 2

int total_num_of_name, mapp[MAX][MAX];
char num_of_name[MAX][MAX];

int min(int a, int b) {
    return a > b ? b : a;
}

///////////////////////////////////////Dijkstra///////////////////////////////////////
int Dijkstra(int start_point, int end_point) {
    
    int dis_from_sp[MAX];  //各个点到起点的最短距离
    fill(dis_from_sp, dis_from_sp + total_num_of_name, INF);  //一开始为INF
    
    bool used_point[MAX];  //各个点是否用过来扩展更新最短路径,这里的点是dis_from_sp中的点
    fill(used_point, used_point + total_num_of_name, false);
    
    dis_from_sp[start_point] = 0;  //起点特殊
    
    while (1) {
        int v = -1;
        for (int i = 0; i < total_num_of_name; i++) {  //从dis_from_sp中找出一个没有用过的而且到起点距离最小的点来更新起点到各个点的最短路径
            if (!used_point[i] && (v == -1 || dis_from_sp[i] < dis_from_sp[v]))
                v = i;
        }
        
        if (v == -1)  //如果还是-1说明dis_from_sp中的点都已经用完了
            break;
        
        used_point[v] = true;
        for (int i = 0; i < total_num_of_name; i++) {  //通过中间点(当前到起点的最短点)来更新最短路径
            dis_from_sp[i] = min(dis_from_sp[i], dis_from_sp[v] + mapp[v][i]);
        }
    }
    return dis_from_sp[end_point];  //最后只需要返回终点到起点的最短路径就可以了
}
///////////////////////////////////////Dijkstra///////////////////////////////////////

int main() {
    
    std::ios::sync_with_stdio(false);
    
    int case_num, n, dis;
    string temp_f, temp_t;
    cin >> case_num;
    
    while (case_num--) {
        cin >> n;
        
        for (int i = 0; i < MAX; i++) {
            for (int j = 0; j < MAX; j++) {
                mapp[i][j] = INF;
            }
        }
        total_num_of_name = 0;
        map<string, int> m;
        
        while (n--) {
            cin >> temp_f >> temp_t >> dis;
            if (m.find(temp_f) == m.end()) {
                m[temp_f] = total_num_of_name++;
            }
            if (m.find(temp_t) == m.end()) {
                m[temp_t] = total_num_of_name++;
            }
            mapp[m[temp_f]][m[temp_t]] = dis < mapp[m[temp_f]][m[temp_t]] ? dis : mapp[m[temp_f]][m[temp_t]];
            mapp[m[temp_t]][m[temp_f]] = dis < mapp[m[temp_t]][m[temp_f]] ? dis : mapp[m[temp_t]][m[temp_f]];
        }
        
        cin >> temp_f >> temp_t;
        if (temp_f == temp_t) {
            cout << 0 << endl;
            continue;
        }
        
        if (m.find(temp_f) == m.end() || m.find(temp_t) == m.end()) {
            cout << -1 << endl;
            continue;
        }
        
        for (int i = 0; i < total_num_of_name; i++) {
            mapp[i][i] = 0;
        }
        
        int ans = Dijkstra(m[temp_f], m[temp_t]);
        
        cout << (ans < INF ? ans : -1) << endl;
    }
    return 0;
}
优化的Dijkstra算法:


#include <stdio.h>
#include <string.h>
#include <iostream>
#include <map>
#include <string>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
#define INF 99999999
#define MAX 115 * 2

//这次也是Dijkstra算法,但是在数值的插入和取最小值方面进行了优化,使用了优先队列,这样经过自动排序后,每次可以直接从队首取得距离起点最小值


int total_num_of_name;//点的总数

struct edges {//用结构体作为二维动态数组的元素存图
    int to;//到达的点
    int diss;//距离
    edges(int new_to, int new_diss): to(new_to), diss(new_diss){}//结构体的初始化函数
};

vector<edges> e[MAX];//用结构体作为二维动态数组的元素存图

typedef pair<int, int> p;//优先队列里的元素,first是到起点距离,second是点号

int Dijkstra(int start_point, int end_point) {
    
    int dis_from_sp[MAX];//到起点的距离
    fill(dis_from_sp, dis_from_sp + total_num_of_name, INF);//初始化
    dis_from_sp[start_point] = 0;//起点的特殊处理
    
    priority_queue<p, vector<p>, greater<p> > q;//优先队列,greater参数让其从小到大排序
    q.push(p(0, start_point));//起点进入
    
    while (!q.empty()) {
        p temp_p = q.top();//获得队首元素,也即是距离起点的最小值
        q.pop();
        int pos = temp_p.second;
        
        if (dis_from_sp[pos] < temp_p.first)//这说明有可能这个点通过其他的中间点获得了比它记录的到起点的更短距离,这时候以它来更新最短距离没有意义(本来就不是最短的)
            continue;
        
        for (int i = 0; i < (int)e[pos].size(); i++) {//在pos能到达的点之间更新
            edges new_edge = e[pos][i];
            
            if (dis_from_sp[new_edge.to] > dis_from_sp[pos] + new_edge.diss) {//如果某个点距离起点的距离比通过中间点的连接的距离大,则要更新
                
                dis_from_sp[new_edge.to] = dis_from_sp[pos] + new_edge.diss;
                
                q.push(p(dis_from_sp[new_edge.to], new_edge.to));//这就相当于没有优化的Dijkstra算法中的dis_from_sp的更新
            }
        }
    }
    
    return dis_from_sp[end_point];
}

int main() {
    
    std::ios::sync_with_stdio(false);
    
    int case_num, n, dis;
    string temp_f, temp_t;
    cin >> case_num;
    
    while (case_num--) {
        
        cin >> n;
        total_num_of_name = 0;
        map<string, int> m;
        for (int i = 0; i < MAX; i++) {//丫的没有初始化WA了n次
            e[i].clear();
        }
         
        while (n--) {
            cin >> temp_f >> temp_t >> dis;
            if (m.find(temp_f) == m.end()) {
                m[temp_f] = total_num_of_name++;
            }
            if (m.find(temp_t) == m.end()) {
                m[temp_t] = total_num_of_name++;
            }
            e[m[temp_f]].push_back(edges(m[temp_t], dis));
            e[m[temp_t]].push_back(edges(m[temp_f], dis));
        }
        
        for (int i = 0; i < total_num_of_name; i++) {//这里是对起点终点相同的点的调整,感觉不要也行吧。。。
            int j;
            for (j = 0; j < (int)e[i].size(); j++) {
                if (e[i][j].to == i) {
                    e[i][j] = edges(i, 0);
                    break;
                }
            }
            if (j >= (int)e[i].size())
                e[i].push_back(edges(i, 0));
        }
        
        
        cin >> temp_f >> temp_t;
        if (temp_f == temp_t) {
            cout << 0 << endl;
            continue;
        }
        
        if (m.find(temp_f) == m.end() || m.find(temp_t) == m.end()) {
            cout << -1 << endl;
            continue;
        }
        
        int ans = Dijkstra(m[temp_f], m[temp_t]);
        
        cout << (ans < INF ? ans : -1) << endl;
    }
    return 0;
}                                
下面是自己的玩玩:如何还原最短路径呢?

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <map>
#include <string>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
#define INF 99999999
#define MAX 115 * 2

//这次也是Dijkstra算法,但是在数值的插入和取最小值方面进行了优化,使用了优先队列,这样经过自动排序后,每次可以直接从队首取得距离起点最小值


int total_num_of_name;//点的总数

struct edges {//用结构体作为二维动态数组的元素存图
    int to;//到达的点
    int diss;//距离
    edges(int new_to, int new_diss): to(new_to), diss(new_diss){}//结构体的初始化函数
};

vector<edges> e[MAX];//用结构体作为二维动态数组的元素存图

typedef pair<int, int> p;//优先队列里的元素,first是到起点距离,second是点号

int pre[MAX];

int Dijkstra(int start_point, int end_point) {
    
    int dis_from_sp[MAX];//到起点的距离
    fill(dis_from_sp, dis_from_sp + total_num_of_name, INF);//初始化
    dis_from_sp[start_point] = 0;//起点的特殊处理
    
    priority_queue<p, vector<p>, greater<p> > q;//优先队列,greater参数让其从小到大排序
    q.push(p(0, start_point));//起点进入
    
    fill(pre, pre + total_num_of_name, -1);
    
    while (!q.empty()) {
        p temp_p = q.top();//获得队首元素,也即是距离起点的最小值
        q.pop();
        int pos = temp_p.second;
        
        if (dis_from_sp[pos] < temp_p.first)//这说明有可能这个点通过其他的中间点获得了比它记录的到起点的更短距离,这时候以它来更新最短距离没有意义(本来就不是最短的)
            continue;
        
        for (int i = 0; i < (int)e[pos].size(); i++) {//在pos能到达的点之间更新
            edges new_edge = e[pos][i];
            
            if (dis_from_sp[new_edge.to] > dis_from_sp[pos] + new_edge.diss) {//如果某个点距离起点的距离比通过中间点的连接的距离大,则要更新
                
                dis_from_sp[new_edge.to] = dis_from_sp[pos] + new_edge.diss;
                
                q.push(p(dis_from_sp[new_edge.to], new_edge.to));//这就相当于没有优化的Dijkstra算法中的dis_from_sp的更新
                
                pre[new_edge.to] = pos;//这里表示,直接到达new_edge.to的最短路径上的点
            }
        }
    }
    
    return dis_from_sp[end_point];
}

string name[MAX];//用来存点的字符名字

void find_shortest_path(int p) {
    
    vector<int> path;
    for (; p != -1; p = pre[p])
        path.push_back(p);//从终点不断地往回找
    reverse(path.begin(), path.end());//倒过来就是从起点到终点了
    
    vector<int> :: iterator pathh;
    for (pathh = path.begin(); pathh != path.end(); pathh++) {
        if (pathh != path.begin())
            cout << " ---> ";
        cout << name[*pathh];
    }
    cout << endl;
}

int main() {
    
    std::ios::sync_with_stdio(false);
    
    int case_num, n, dis;
    string temp_f, temp_t;
    cin >> case_num;
    
    while (case_num--) {
        
        cin >> n;
        total_num_of_name = 0;
        map<string, int> m;
        for (int i = 0; i < MAX; i++) {//丫的没有初始化WA了n次
            e[i].clear();
            name[i].clear();
        }
        
        while (n--) {
            cin >> temp_f >> temp_t >> dis;
            if (m.find(temp_f) == m.end()) {
                m[temp_f] = total_num_of_name++;
                name[total_num_of_name - 1] = temp_f;
            }
            if (m.find(temp_t) == m.end()) {
                m[temp_t] = total_num_of_name++;
                name[total_num_of_name - 1] = temp_t;
            }
            e[m[temp_f]].push_back(edges(m[temp_t], dis));
            e[m[temp_t]].push_back(edges(m[temp_f], dis));
        }
        
        for (int i = 0; i < total_num_of_name; i++) {//这里是对起点终点相同的点的调整,感觉不要也行吧。。。
            int j;
            for (j = 0; j < (int)e[i].size(); j++) {
                if (e[i][j].to == i) {
                    e[i][j] = edges(i, 0);
                    break;
                }
            }
            if (j >= (int)e[i].size())
                e[i].push_back(edges(i, 0));
        }
        
        
        cin >> temp_f >> temp_t;
        if (temp_f == temp_t) {
            cout << 0 << endl;
            continue;
        }
        
        if (m.find(temp_f) == m.end() || m.find(temp_t) == m.end()) {
            cout << -1 << endl;
            continue;
        }
        
        int ans = Dijkstra(m[temp_f], m[temp_t]);
        
        cout << (ans < INF ? ans : -1) << endl;
        
        find_shortest_path(m[temp_t]);
    }
    return 0;
}        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值