天梯赛L2-001 紧急救援 (强化版pat甲级1003 Emergency)

21 篇文章 0 订阅
8 篇文章 0 订阅

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

pat甲级1003就是这题去掉第二行输出。

解题思路

先用dijkstra算法求s到d的最短路,然后跑一遍dfs找最短路径条数和最大救援队数量。

dfs代码如下

void dfs(int x)
{
	dc[x] ++;   //到点x的最短路有几种 
	for(int i = 0; i < n; i ++){
		if(!f[i] && dis[x] + g[x][i] == dis[i]){     //到点i的最短路经过边g[x][i] 
			f[i] = true;
			if(dv[i] < dv[x] + v[i])
				dv[i] = dv[x] + v[i];   //到i时的最大救援队数量 
			dfs(i);
			f[i] = false;   //回溯 
		}
	}
}

到这里pat甲级1007的问题已经解决了。然后就是求路径,在跑一个dfs找路径记录下然后输出,就好了。

dfs代码如下

void dfs2(int x)
{
	if(x == s)   //s为起始点,从终点开始跑 
		return;
	for(int i = 0; i < n; i ++){
		if(dis[i] + g[i][x] == dis[x] && dv[i] + v[x] == dv[x]){
			ans[top ++] = i;   //存路径 
			dfs2(i);
			break;
		}
	}
}

完整代码如下

#include <iostream>
#include <cstring>
#include <stack>
#define INF 0x3f3f3f3f
using namespace std;
int v[505];
int g[505][505];
bool vis[505];
int dis[505];
int dv[505];
int dc[505];
int n, m, s, d;
bool f[505];
void dfs(int x)
{
	dc[x] ++;   //到点x的最短路有几种 
	for(int i = 0; i < n; i ++){
		if(!f[i] && dis[x] + g[x][i] == dis[i]){     //到点i的最短路经过边g[x][i] 
			f[i] = true;
			if(dv[i] < dv[x] + v[i])
				dv[i] = dv[x] + v[i];   //到i时的最大救援队数量 
			dfs(i);
			f[i] = false;   //回溯 
		}
	}
}
int ans[505];
int top;
void dfs2(int x)
{
	if(x == s)   //s为起始点,从终点开始跑 
		return;
	for(int i = 0; i < n; i ++){
		if(dis[i] + g[i][x] == dis[x] && dv[i] + v[x] == dv[x]){
			ans[top ++] = i;   //存路径 
			dfs2(i);
			break;
		}
	}
}
int main()
{
	while(cin >> n >> m >> s >> d){
		for(int i = 0; i < n; i ++){
			for(int j = 0; j < n; j ++)
				g[i][j] = INF;
			g[i][i] = 0;
		}
		for(int i = 0; i < n; i ++)
			cin >> v[i];
		for(int i = 0; i < m; i ++){
			int l, s, w;
			cin >> l >> s >> w;
			if(g[l][s] > w){
				g[l][s] = w;
				g[s][l] = w;
			}
				
		}
		memset(vis, 0, sizeof(vis));
		memset(f, 0, sizeof(f));
		memset(dc, 0, sizeof(dc));
		vis[s] = true;
		for(int i = 0; i < n; i ++){
			dis[i] = g[s][i];
			dv[i] = v[i];
		}			
		for(int i = 1; i < n; i ++){
			int minn = INF;
			int k = s;
			for(int j = 0; j < n; j ++){
				if(!vis[j] && dis[j] < minn){
					minn = dis[j];
					k = j;
				}
			}
			vis[k] = true;
			if(k == d)
				break;
			for(int j = 0; j < n; j ++){
				if(!vis[j] && dis[k] + g[k][j] < dis[j]){
					dis[j] = dis[k] + g[k][j];							
				}
			}
		}
		f[s] = true;
		dfs(s);
		cout << dc[d] << " " << dv[d] << endl;
		memset(ans, 0, sizeof(ans));
		top = 0;
		ans[top ++] = d;
		dfs2(d);
		for(int i = top - 1; i > 0; i --)
			cout << ans[i] << " ";
		cout << ans[0] << endl;
	}
	return 0;
}

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值