Week2Day4B:最短路练习【2023 安全创客实践训练|笔记】

内容为武汉大学国家网络安全学院2022级大一第三学期“996”实训课程中所做的笔记,仅供个人复习使用,如有侵权请联系本人,将于15个工作日内将博客设置为仅粉丝可见。
 


骑车比赛

  • 时间限制:1000ms
  • 内存限制:131072K
  • 语言限制:C语言

蒜头君准备去参加骑车比赛,比赛在 n 个城市间进行,编号从 1 到 n。选手们都从城市 1 出发,终点在城市 n。 

已知城市间有 m 条道路,每条道路连接两个城市,注意道路是双向的。现在蒜头君知道了他经过每条道路需要花费的时间,他想请你帮他计算一下,他这次比赛最少需要花多少时间完成。

输入格式

第一行输入两个整数 n,m(1≤n≤1,000,1≤m≤5,000),分别代表城市个数和道路总数。接下来输入 m 行,每行输入三个数字 a,b,c(1≤a,b≤n,1≤c≤200),分别代表道路的起点和道路的终点,以及蒜头君骑车通过这条道路需要花费的时间。保证输入的图是连通的。

输出格式

输出一行,输出一个整数,输出蒜头君完成比赛需要的最少时间。

格式说明

输出时每行末尾的多余空格,不影响答案正确性

样例输入
5 6
1 2 2
2 3 3
2 5 5
3 4 2
3 5 1
4 5 1
样例输出
6

 

 我的答案

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXN 1005
#define INF 0x3f3f3f3f

int n, m;
int g[MAXN][MAXN];
int dist[MAXN];
int vis[MAXN];

void dijkstra(int s) {
    memset(dist, 0x3f, sizeof(dist));
    dist[s] = 0;
    for (int i = 1; i <= n; i++) {
        int x = 0;
        for (int j = 1; j <= n; j++) {
            if (!vis[j] && (x == 0 || dist[j] < dist[x])) {
                x = j;
            }
        }
        vis[x] = 1;
        for (int y = 1; y <= n; y++) {
            dist[y] = dist[y] < dist[x] + g[x][y] ? dist[y] : dist[x] + g[x][y];
        }
    }
}

int main() {
    scanf("%d%d", &n, &m);
    memset(g, 0x3f, sizeof(g));
    while (m--) {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        g[a][b] = g[b][a] = c;
    }
    dijkstra(1);
    printf("%d\n", dist[n]);
    return 0;
}

迷阵突围(Day10 完成)

  • 时间限制:1000ms
  • 内存限制:131072K
  • 语言限制:C语言

蒜头君陷入了坐标系上的一个迷阵,迷阵上有 n 个点,编号从 1 到 n。蒜头君在编号为 1 的位置,他想到编号为 n 的位置上。蒜头君当然想尽快到达目的地,但是他觉得最短的路径可能有风险,所以他会选择第二短的路径。现在蒜头君知道了 n 个点的坐标,以及哪些点之间是相连的,他想知道第二短的路径长度是多少。

注意,每条路径上不能重复经过同一个点

输入格式

第一行输入两个整数 n(1≤n≤200) 和 m,表示一共有 n 个点和 m 条边。

接下来输入 n 行,每行输入两个整数 x_i​,y_i​(−500≤x_i​,y_i​≤500),代表第 i 个点的坐标。

接下来输入 m 行,每行输入两个整数 p_j​,q_j​ (1≤p_j​,q_j​≤n),表示点 p_j​ 和点 q_j​ 之间相连。

输出格式

输出一行,输出包含一个数,表示第二短的路径长度(小数点后面保留两位),如果第一短路径有多条,则答案就是第一最短路径的长度;如果第二最短路径不存在,则输出 −1。

格式说明

输出时每行末尾的多余空格,不影响答案正确性

样例输入
3 3
1 1
2 2
3 2
1 2
2 3
1 3
样例输出
2.41

 题解:先求出一条最短路径,依次枚举上面的每条边删除,然后依次求最短路,并得到其中的最小值。

我的答案

#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>

#define INF 0x3f3f3f3f
#define N 210
#define M 40100

struct edge {
    int v, next, f;
    double w;
} e[M];

int p[N], eid;
int pre[N];
double d[N];
int x[N], y[N];
int vis[N];
int n, m;

void init() {
    memset(p, -1, sizeof(p));
    eid = 0;
}

void insert(int u, int v, double w) {
    e[eid].v = v;
    e[eid].w = w;
    e[eid].next = p[u];
    e[eid].f = 1;
    p[u] = eid++;
}

double dis(int i, int j) {
    return sqrt((x[i]-x[j])*(x[i]-x[j]) + (y[i]-y[j])*(y[i]-y[j]));
}

void dijkstra(int s, int del) {
    int i, j, u, v;
    double min, w;
    for(i=1; i<=n; i++) {
        d[i] = INF;
        vis[i] = 0;
        pre[i] = -1;
    }
    d[s] = 0;
    for(i=1; i<=n; i++) {
        min = INF;
        u = -1;
        for(j=1; j<=n; j++) {
            if(!vis[j] && d[j]<min) {
                min = d[j];
                u = j;
            }
        }
        if(u == -1) return;
        vis[u] = 1;
        for(j=p[u]; j!=-1; j=e[j].next) {
            if(j == del || j == (del^1)) continue;
            v = e[j].v;
            w = e[j].w;
            if(!vis[v] && d[v]>d[u]+w) {
                d[v] = d[u] + w;
                pre[v] = j;
            }
        }
    }
}

int main() {
    int i, u, v;
    double ans, tmp;
    scanf("%d%d", &n, &m);
    init();
    for(i=1; i<=n; i++) {
        scanf("%d%d", &x[i], &y[i]);
    }
    for(i=0; i<m; i++) {
        scanf("%d%d", &u, &v);
        insert(u, v, dis(u, v));
        insert(v, u, dis(u, v));
    }
    ans = INF;
    dijkstra(1, -1);
    for(i=pre[n]; i!=-1; i=pre[e[i^1].v]) {
        tmp = e[i].w;
        e[i].w = e[i^1].w = INF;
        dijkstra(1, -1);
        if(d[n] < ans) ans = d[n];
        e[i].w = e[i^1].w = tmp;
    }
    if(ans == INF) printf("-1\n");
    else printf("%.2lf\n", ans);
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值