PTA 城市间紧急救援 分数 25 作者 陈越 单位 浙江大学

作为一个城市的应急救援队伍的负责人,你有一张特殊的全国地图。在地图上显示有多个分散的城市和一些连接城市的快速道路。每个城市的救援队数量和每一条连接两个城市的快速道路长度都标在地图上。当其他城市有紧急求助电话给你的时候,你的任务是带领你的救援队尽快赶往事发地,同时,一路上召集尽可能多的救援队。
输入格式:

输入第一行给出4个正整数N、M、S、D,其中N(2≤N≤500)是城市的个数,顺便假设城市的编号为0 ~ (N−1);M是快速道路的条数;S是出发地的城市编号;D是目的地的城市编号。

第二行给出N个正整数,其中第i个数是第i个城市的救援队的数目,数字间以空格分隔。随后的M行中,每行给出一条快速道路的信息,分别是:城市1、城市2、快速道路的长度,中间用空格分开,数字均为整数且不超过500。输入保证救援可行且最优解唯一。
输出格式:

第一行输出最短路径的条数和能够召集的最多的救援队数量。第二行输出从S到D的路径中经过的城市编号。数字间以空格分隔,输出结尾不能有多余空格。
输入样例:

4 5 0 3
20 30 40 10
0 1 1
1 3 2
0 3 3
0 2 2
2 3 2

输出样例:

2 60
0 1 3

代码长度限制
16 KB
时间限制
400 ms
内存限制
64 MB

答案:

# include <stdio.h>
# include <stdlib.h>
# define INF 999
# define MAXV 510

int Path[MAXV], Dist[MAXV], Right[MAXV];//Path存储最短路径上的前驱节点,Dist存储起点到各节点的最短路径权值和,Right存储起点到各节点的节点权值和
int cnt[MAXV];//存储起点到各节点的最短路径数量

typedef struct GNode
{
	int V[MAXV];//节点
	int E[MAXV][MAXV];//邻接矩阵
	int W[MAXV];//节点权值
	int n, m;
}Graph;

Graph* CreateGraph(int n, int m)
{
	Graph* G;
	G = (Graph*)malloc(sizeof(Graph));
	int city1, city2, length;//输入数据
	G->n = n;
	G->m = m;
	for (int i = 0; i < n; i++)G->V[i] = i;//初始化
	for (int i = 0; i < n; i++)//初始化
	{
		for (int j = 0; j < n; j++)G->E[i][j] = INF;
	}

	for (int i = 0; i < n; i++)scanf("%d", &G->W[i]);//输入节点权值
	for (int i = 0; i < m; i++)//输入邻接矩阵
	{
		scanf("%d %d %d", &city1, &city2, &length);
		G->E[city1][city2] = length;
		G->E[city2][city1] = length;
	}

	return G;
}

void Dijkstra(Graph* G,int s)
{
	int final[MAXV];//标记数组,表示节点是否已经加入最短路径树
	int k;//后面会用
	for (int i = 0; i < G->n; i++)//初始化
	{
		final[i] = 0;//全部顶点初始化未加入最短路径树状态
		Dist[i] = G->E[s][i];//将与s有连线的节点加上权值,其余为INF
		Path[i] = s;//初始化路径数组
		cnt[i] = 1;//初始时默认各节点都仅有一条最短路径
		Right[i] = 0;//默认都为0
	}
	Dist[s] = 0;//s至s路径为0
	final[s] = 1;//s加入最短路径树
	Right[s] = G->W[s];//s到s的权值和为起点的权值

	for (int i = 1; i < G->n; i++)//开始主循环,每次求得s到某个顶点i的最短路径。因为s已加入最短路径树,还有n - 1个未加入,故从i = 1开始
	{
		int min = INF;//当前所知离s顶点的最近距离,初始为最大
		for (int j = 0; j < G->n; j++)
		{
			if (!final[j] && Dist[j] < min)//找到未加入最短路径树的节点距最短路径树的最小距离
			{
				k = j;
				min = Dist[j];
			}
		}
		
        if (i == 1)//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!因为i==1时找到的节点并未对Right进行处理,并且距离==min的节点也不一定只有一个,所以要对此情况额外处理一下
        {
            for (int j = 0; j < G->n; j++)
            {
                if (!final[j] && Dist[j] == min && Right[j] > Right[k])//找到距离等于min并且权值最大的节点(根据题目要求“一路上召集尽可能多的救援队”)
                {
                    k = j;
                }
            }
            for (int j = 0; j < G->n; j++)//对每个距离等于min的节点的Right值进行处理
            {
                if (!final[j] && Dist[j] == min)
                {
                    Right[j] = G->W[s] + G->W[j];
                }
            }
        }
        
        final[k] = 1;//将节点加入最短路径树

		for (int j = 0; j < G->n; j++)//修正当前最短路径及距离、权值
		{
			if (!final[j] && (min + G->E[k][j] < Dist[j]))//如果经过k顶点到j顶点的路径比到j顶点本来的路径短的话
			{
				//说明找到了更短的路径
				Dist[j] = min + G->E[k][j];
				Path[j] = k;
				Right[j] = Right[k] + G->W[j];
				cnt[j] = cnt[k];
			}
			else if (!final[j] && (min + G->E[k][j] == Dist[j]))//如果经过k顶点到j顶点的路径与到j顶点本来的路径相等的话
			{
				cnt[j] += cnt[k];//说明有多条最短路径,这里修改到j节点的最短路径的数目
				if (Right[j] < Right[k] + G->W[j])//这里选择能召集最多救援队的路径
				{
					Right[j] = Right[k] + G->W[j];
					Path[j] = k;
				}
			}
		}
	}
}

void Print(int s, int d)
{
	printf("%d %d\n", cnt[d], Right[d]);
	int Stack[MAXV];
	int top = -1;
	int k = d;
	Stack[++top] = k;
	while (1)
	{
		if (k == s)break;
		Stack[++top] = Path[k];
		k = Path[k];
	}
	while (top != -1)
	{
		if (top != 0)printf("%d ", Stack[top]);
		else printf("%d", Stack[top]);
		top--;
	}
}

int main()
{
	int n, m, s, d;
	scanf("%d %d %d %d", &n, &m, &s, &d);
	Graph* G;
	G = CreateGraph(n, m);
	Dijkstra(G, s);
	Print(s, d);
	return 0;
}

下面代码中的Dijkstra算法处理得更简单,但是封装得不太好,上面代码的Dijkstra算法可以借鉴下面。

值得提的一点是,做工程项目时一定要按照第一段代码去写,要封装好,这样才能复用,才足够稳定。
而参加蓝桥杯、acm等竞赛时,可以按照第二段代码的方式去写,因为比赛就是要求快速解题,一次通过就行,不会再复用。

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

#define N 1010

int n, m, S, D;
int g[N][N], w[N];  // g 存储图的边权,w 存储每个节点的权值
int st[N];          // 标记数组,表示节点是否已经加入最短路径树
int dist[N], r[N], path[N];  // dist 存储起点到各节点的最短距离,r 存储起点到各节点的权值和,path 存储最短路径上的前驱节点
int cnt[N];  // cnt 存储起点到各节点的最短路径数量

void dijkstra(int u, int s) {
    memset(dist, 0x3f, sizeof(dist));  // 初始化距离数组为无穷大
    dist[u] = 0;  // 起点到自身的距离为0
    r[u] = w[u];  // 起点到自身的权值和为起点的权值
    for (int i = 0; i < n; i++) 
    {
        int t = -1;
        for (int j = 0; j < n; j++) {
            if (!st[j] && (t == -1 || dist[t] > dist[j]))
                t = j;  // 选择未加入最短路径树的距离最小的节点
        }
        st[t] = 1;  // 将节点加入最短路径树

        for (int j = 0; j < n; j++) {
            if (dist[j] > dist[t] + g[t][j]) {
                dist[j] = dist[t] + g[t][j];
                path[j] = t;
                r[j] = r[t] + w[j];
                cnt[j] = cnt[t];
            } else if (dist[j] == dist[t] + g[t][j]) {
                cnt[j] += cnt[t];
                if (r[j] < r[t] + w[j]) {
                    r[j] = r[t] + w[j];
                    path[j] = t;
                }
            }
        }
    }
    printf("%d %d\n", cnt[s], r[s]);  // 输出最短路径数量和最短路径权值和
    int q[N];
    int k = s, idx = 0;
    q[idx++] = k;
    while (1) {
        if (k == u)
            break;
        q[idx++] = path[k];
        k = path[k];
    }
    for (int i = idx - 1; i >= 0; i--) {
        if (i != 0)
            printf("%d ", q[i]);
        else
            printf("%d\n", q[i]);
    }
}

int main() {
    scanf("%d%d%d%d", &n, &m, &S, &D);
    for (int i = 0; i < n; i++)
        scanf("%d", &w[i]), cnt[i] = 1;

    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(S, D);  // 调用Dijkstra算法求解最短路径

    return 0;
}
天梯图书阅览室是由陈越教授在浙江大学编写的一款用Python编程语言创建的阅览室系统。该系统的主要功能包括借阅图书、归还图书、查询图书等。 首先,用户可以通过系统进行图书的借阅。用户只需输入图书的编号和借阅数量,系统会检查图书是否可借,并记录借阅信息。同时,系统会显示借阅成功的提示信息,方便用户了解借阅情况。 其次,用户在完成阅读后,可以通过系统归还图书。用户只需输入图书的编号,系统会检查图书是否逾期以及是否借阅过,若符合条件,系统会更新图书的状态为已归还,并提醒用户归还成功。 此外,用户还可以通过系统进行图书的查询。用户可以根据图书的名称、作者或关键字进行查询,系统会提供相关的图书信息,包括图书的名称、作者、出版日期等。同时,用户还可以查看图书的借阅情况,知道当前是否有其他人正在借阅该图书。 天梯图书阅览室还提供了管理员功能。管理员可以对图书进行管理,包括添加图书、删除图书和修改图书信息。管理员还可以查看所有图书的借阅情况,并进行逾期管理。 总结来说,天梯图书阅览室是一款功能齐全的阅览室系统,通过使用Python编程语言,实现了用户的借阅、归还和查询功能,以及管理员的图书管理和逾期管理功能。这个系统可以极大地提高图书管理的效率和便利性,方便用户进行图书的借阅与归还。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

约束112

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值