PAT甲级(Advanced Level)1003(Dijkstra)

作为一个城市的紧急救援队队长,你得到了一张你的国家的特殊地图。该地图显示了几个由一些道路连接的分散的城市。每个城市的救援队的数量和任何一对城市之间的每条道路的长度都标在地图上。当其他城市向你发出紧急呼叫时,你的工作是带领你的人尽快赶到那个地方,同时在路上尽可能多地召集人手。

输入

每个输入文件包含一个测试案例。对于每个测试案例,第一行包含4个正整数。N(<=500)--城市的数量(而且城市的编号从0到N-1),M--道路的数量,C1和C2--你目前所在的城市和你必须保存的城市,分别。下一行包含N个整数,其中第i个整数是第i个城市中救援队的数量。然后是M行,每行描述一条路,有三个整数c1、c2和L,分别是由一条路连接的一对城市和该路的长度。保证至少存在一条从C1到C2的路径。

输出

对于每个测试案例,在一行中打印两个数字:C1和C2之间不同的最短路径的数量,以及你可能召集的最大救援队的数量。
一行中的所有数字必须由一个空格隔开,行末不允许有多余的空格。

输入样本

5 6 0 2
1 2 1 5 3
0 1 1
0 2 2
0 3 1
1 2 1
2 4 1
3 4 1

输出样本

2 4

(通过www.DeepL.com/Translator(免费版)翻译)

《原题传送门》https://pintia.cn/problem-sets/994805342720868352/exam/problems/994805523835109376题目大意:从给定的起始点出发到给定终点的所有路中,找到花费时间最少的路线的条数,且找到这些路线中能召集到的最多的人。

分析:先明确城市为点,城市里的人数为点权,城市到城市的路为边,城市到城市所花费时间为边权。我们要做的就是找到边权最小的路线的数目,以及这些路线的点的点权和的最大值。一下提供两种做法:

        做法1:对让图以c1为起点进行dfs,等到递归到c2时进行条件判断,从而找到所需要最短路径数量cnt和召集到的最多人手MaxAW。

#include<iostream>
using namespace std;
int n,c1,c2,amg[501][501],visit[501],weight[501],cnt;
int MaxPw, MinAW=99999999;
void dfs(int v,int pw ,int aw) {
	visit[v] = 1;
	if (v == c2) {
		if (aw < MinAW) {
			MinAW = aw;
			MaxPw = pw;
			cnt = 1;
		}
		else if (aw == MinAW) {
			if (pw > MaxPw) {
				MaxPw = pw;
			}
			cnt++;
		}
		visit[v] = 0;
		return;
	}
	for (int i = 0; i < n; i++) {
		if (visit[i] == 0 && amg[v][i] != 0) {
			dfs(i, pw + weight[i], aw + amg[v][i]);
		}
	}
	visit[v] = 0;
}
int main() {
	int  m;
	cin >> n >> m >> c1 >> c2;
	for (int i = 0; i < n; i++)
		cin >> weight[i];
	for (int i = 0; i < m; i++) {
		int a, b, w;
		cin >> a >> b >> w;
		amg[a][b] = amg[b][a] = w;
	}
	dfs(c1, weight[c1], 0);
	cout << cnt << " " << MaxPw;
	return 0;
}

(以下为对柳神的代码进行讲解)

《柳神原帖传送门》

做法2:dijkstra,建立数组num[ v ]记录c1到v的路线数目,dis[ v ]记录c1到v所需时间,w[ v ]记c1到v所能 召集的人数。每次对边进行松弛时进行条件判断和操作,注意这里的num[v ]数组的操作是由于c1到v的路线不止一条。

 

#include <iostream>
#include <algorithm>
using namespace std;
int n, m, c1, c2;
int e[510][510], weight[510], dis[510], num[510], w[510];
bool visit[510];
const int inf = 99999999;
int main() {
    scanf("%d%d%d%d", &n, &m, &c1, &c2);
    for (int i = 0; i < n; i++)
        scanf("%d", &weight[i]);
    fill(e[0], e[0] + 510 * 510, inf);
    fill(dis, dis + 510, inf);
    int a, b, c;
    for (int i = 0; i < m; i++) {
        scanf("%d%d%d", &a, &b, &c);
        e[a][b] = e[b][a] = c;
    }
    dis[c1] = 0;
    w[c1] = weight[c1];
    num[c1] = 1;
    for (int i = 0; i < n; i++) {
        int u = -1, minn = inf;
        for (int j = 0; j < n; j++) {
            if (visit[j] == false && dis[j] < minn) {
                u = j;
                minn = dis[j];
            }
        }
        if (u == -1) break;
        visit[u] = true;
        for (int v = 0; v < n; v++) {
            if (visit[v] == false && e[u][v] != inf) {
                if (dis[u] + e[u][v] < dis[v]) {
                    dis[v] = dis[u] + e[u][v];
                    num[v] = num[u];
                    w[v] = w[u] + weight[v];
                }
                else if (dis[u] + e[u][v] == dis[v]) {
                    num[v] = num[v] + num[u];
                    if (w[u] + weight[v] > w[v])
                        w[v] = w[u] + weight[v];
                }
            }
        }
    }
    printf("%d %d", num[c2], w[c2]);
    return 0;
}

总结:做法1为暴力做法做法效率不高可以作为做题毫无头绪的一种硬解,建议学一下手听说可以在比赛中吃烂分,但是不能沉迷于此类解法。

做法1:

 做法2:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值