PAT L2-001 紧急救援 (25 分)

L2-001 紧急救援 (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

#include <bits/stdc++.h>
using namespace std;
const int N = 510;
int n, m, s, d;
int dis[N], mvp[N][N], cnt[N], a[N], ans[N], pre[N];
bool st[N];

void print(int k) {
	if (k == s) {
		printf("%d", s);
		return;
	}
	print(pre[k]);
	printf(" %d", k);
}
void dijkstra(){
	dis[s] = 0; cnt[s] = 1; ans[s] = a[s];
	for (int i = 0; i < n; i++) {
		int t = -1;
		for (int j = 0; j < n; j++)
			if (!st[j] && (t == -1 || dis[t] > dis[j]))
				t = j;
		st[t] = 1;
		for (int j = 0; j < n; j++) {
			if (dis[j] > dis[t] + mvp[t][j]) {
				dis[j] = dis[t] + mvp[t][j];
				cnt[j] = cnt[t];
				ans[j] = ans[t] + a[j];
				pre[j] = t;
			}
			else if (dis[j] == dis[t] + mvp[t][j]) {
				cnt[j] += cnt[t];
				if (ans[j] < ans[t] + a[j]) {
					ans[j] = ans[t] + a[j];
					pre[j] = t;
				}
			}
		}
	}
}
int main()
{
	memset(dis, 0x3f, sizeof dis);
	memset(mvp, 0x3f, sizeof mvp);
	scanf("%d%d%d%d", &n, &m, &s, &d);
	for (int i = 0; i < n; i++)
		scanf("%d", &a[i]);
	while (m--) {
		int x, y, z;
		scanf("%d%d%d", &x, &y, &z);
		mvp[x][y] = mvp[y][x] = z;
	}
	dijkstra();
	printf("%d %d\n", cnt[d], ans[d]);
	print(d);
}

这是o(n^2)的dijkstra算法,数据范围正好合适。
如果用o(nlogn)的堆优化版dijkstra,也可以a过
但是如果用spfa的话,只有18分,第二个测试点数据死活调不过去,有无看到的好心人解释一下或者调试一下,是因为dijkstra每次取的都是最短的最优解,spfa每次只是松弛一下不是最优吗?

#include<bits/stdc++.h>
using namespace std;
const int N = 500 + 10;
int n, m, s, d;
int mvp[N][N], a[N];
int vis[N], cnt[N], dis[N], pre[N], ans[N];
void spfa(){
	queue<int> q;
	q.push(s);
	vis[s] = 1, dis[s] = 0, cnt[s] = 1, ans[s] = a[s];
	while(!q.empty()) {
		int u = q.front(); q.pop(); vis[u] = 0;
		for (int i = 0; i < n; i++) {
			if (dis[i] > dis[u] + mvp[u][i]) {
				pre[i] = u;
				dis[i] = dis[u] + mvp[u][i];
				cnt[i] = cnt[u];
				ans[i] = ans[u] + a[i];
				if (!vis[i]) {
					vis[i] = 1, q.push(i);
				}
			}
			else if(dis[i] == dis[u] + mvp[u][i]) {
				cnt[i] += cnt[u];
				if (ans[u] + a[i] > ans[i]) {
					ans[i] = ans[u] + a[i];
					pre[i] = u;
				}
				if (!vis[i]) {
					vis[i] = 1, q.push(i);
				}
			}
		}
	}
}
void printpath(int v){
	if (v == s) {
		printf("%d", v);
		return;
	}
	printpath(pre[v]);
	printf(" %d", v);
}
int main()
{
	memset(mvp, 0x3f, sizeof mvp);
	memset(dis, 0x3f, sizeof dis);
	cin>>n>>m>>s>>d;
	for (int i = 0; i < n; i++)
		cin>>a[i];
	for (int i = 0; i < m; i++) {
		int x, y, z;
		cin>>x>>y>>z;
		mvp[x][y] = mvp[y][x] = z;
	}
	spfa();
	printf("%d %d\n", cnt[d], ans[d]);
	printpath(d); 
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值