L2-001. 紧急救援
作为一个城市的应急救援队伍的负责人,你有一张特殊的全国地图。在地图上显示有多个分散的城市和一些连接城市的快速道路。每个城市的救援队数量和每一条连接两个城市的快速道路长度都标在地图上。当其他城市有紧急求助电话给你的时候,你的任务是带领你的救援队尽快赶往事发地,同时,一路上召集尽可能多的救援队。
输入格式:
输入第一行给出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
解题思路
借助辅助数组
now[i] 表示 到 i 点最多能集合多少人
num[i] 表示 到 i 点有多少种最短的路
在Dijkastra算法中更新两者的值。
只有长度相等的时候,才去考虑怎么样集结更多人,并且此时到这个点的方式又多出很多种,所以多加有个 if 判断即可
循环实现代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <string>
#include <cstring>
#include <set>
#include <queue>
#include <stack>
#include <stdlib.h>
#include <map>
using namespace std;
const int maxn = 510;
const int INF = 0x3f3f3f3f;
int g[maxn][maxn];
int weight[maxn];
int d[maxn];
int num[maxn];///到当前结点最短路多少条
int now[maxn];///到当前节点最多的人数
int pre[maxn];
bool visit[maxn];
int n,m,s,de;
void disjk(){
memset(d,INF,sizeof(d));
memset(visit,0,sizeof(visit));
d[s] = 0;
num[s] = 1;
now[s] = weight[s];
while(1){
int u = -1;
///从未选取的点中找一个离起点最近的点
for(int i = 0; i < n; ++i){
if(visit[i]) continue;
if(u == -1 || d[i] < d[u])
u = i;
}
if(u==-1) return;
visit[u] = true;
///更新和这个点相接的点
for(int i = 0;i < n; ++i){
if(d[u]+g[u][i] < d[i]){///短为第一要素
d[i] = d[u]+g[u][i];
num[i] = num[u];
now[i] = now[u]+weight[i];
pre[i] = u;
}
///如果长度相等,才考虑越多人越好
else if(d[u]+g[u][i] == d[i]){
num[i] += num[u];///通过这两种达到i的方式各有num[i]和num[u]种,相加
if(now[i] < now[u] + weight[i]){
now[i] = now[u] + weight[i];
pre[i] = u;
}
}
}
}
}
void dfs(int x){
if(x == s) printf("%d",x);
else{
dfs(pre[x]);
printf(" %d",x);
}
}
int main(){
scanf("%d%d%d%d",&n,&m,&s,&de);
memset(g,INF,sizeof(g));
int u,v,w;
for(int i = 0; i < n; ++i) {scanf("%d",&weight[i]);}
for(int i = 0; i < m; ++i){
scanf("%d%d%d",&u,&v,&w);
g[u][v] = w;
g[v][u] = w;
}
disjk();
printf("%d %d\n",num[de],now[de]);
dfs(de);
return 0;
}
优先队列实现代码:
#include <cstring>
#include <cstdio>
#include <iostream>
#include <vector>
#include <queue>
#include <set>
#include <algorithm>
#include <math.h>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 500+10;
typedef pair<int,int> P;
struct edge{
int to ,cost;
};
vector <edge> G[maxn];
int cnt[maxn];///路条数
int num[maxn];///人数
int weight[maxn];
int dis[maxn];
int pre[maxn];
int s;
void disjk(int s){
memset(cnt,0,sizeof(cnt));
memset(dis,INF,sizeof(dis));
priority_queue <P,vector<P>, greater<P> > que;
dis[s] = 0;
cnt[s] = 1;
num[s] = weight[s];
que.push(P(0,s));
while(!que.empty()){
P p = que.top();
que.pop();
int v = p.second, d = p.first;
if(dis[v]<d) continue;
for(int i = 0;i<G[v].size();++i){
edge &e = G[v][i];
int d2 = d+e.cost;
if(d2 < dis[e.to]) {
dis[e.to] = d2;
pre[e.to] = v;
cnt[e.to] = cnt[v];
num[e.to] = num[v]+weight[e.to];
que.push(P(dis[e.to],e.to));
}else if( d2==dis[e.to]){
cnt[e.to] += cnt[v];
if(num[v]+weight[e.to]>num[e.to]){
num[e.to] = num[v]+weight[e.to];
pre[e.to] = v;
}
}
}
}
}
void dfs(int x){
if(x == s) printf("%d",x);
else{
dfs(pre[x]);
printf(" %d",x);
}
}
int main(){
int n,m,de;
scanf("%d%d%d%d",&n,&m,&s,&de);
int u,v,w;
for(int i = 0; i < n; ++i) {scanf("%d",&weight[i]);}
for(int i = 0; i < m; ++i){
scanf("%d%d%d",&u,&v,&w);
G[u].push_back((edge){v,w});
G[v].push_back((edge){u,w});
}
disjk(s);
printf("%d %d\n",cnt[de],num[de]);
dfs(de);
return 0;
}