紧急救援_最短路径的小变形

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
题目原文

1. 这道题的关键在于更新时怎么处理?

首先设立一个int num[] 表示从出发点到i的最短路径的条数(因为有些最短路径长度相同)

再设立两个数组 int tn[], ts[] 表示 : 某个城市的救援队伍数目   和     从源点出发,经最短路径, 走到 i 点处时,得到的救援队伍数

用一遍Dijkstra算法。更新的时候是重点:

  当判定dist[u] + e[u][v] < dist[v]的时候:

    更新dist[v],还要更新num[v] = num[u], ts[v] = tn[v] + ts[u];

  当dist[u] + e[u][v] == dist[v]的时候,意味着出现了相同的最短路径(当前认为的最短路径)

    更新num[v] += num[u],即意味着到v这一点的方法数又多了 num[u] 种, 而且判断一下是否 ts[v]更小,如果更小了就更新ts[v] = tn[v] + ts[u];   其实,这个点权你是可以理解成另外个层次的"最短路径的(一样处理思想)",只不过这个层次是建立在所经过的路径最短之上的...
再设立一个pre[i]表示最短路径的前一个结点,在dis[u] + e[u][v] <= dis[v]的时候更新pre[v] = u(即虽然之前是按照最短路径来设置路径,但现在遇到了一条同样最短,但是救援队伍更多的路了,需要改变一下路径)

最后打印路径即可.

2. 其次:初始化也很重要!

dist, path, used这些按照 Disjkstra算法来就好

主要是

num[i], 如果与源点邻接, 初值应设为1,代表认为:现在确定的暂时的最短路径条数是1

ts[i],如果与源点邻接,初值应该是:源点队伍数(点权) + i点的队伍数(点权),意味着,如果你要到i这条路, 那么到i,你就会获得这么多的救援队伍.

 1 #include<iostream>
 2 #include<memory.h>
 3 #include<stack>
 4 using namespace std;
 5 
 6 int const maxn = 510;
 7 int const maxValue = 0x3f3f3f3f;
 8 int cost[maxn][maxn];                   // 邻接矩阵
 9 int d[maxn];                            // dist
10 int used[maxn];                         // 记录是否加入集合
11 int pre[maxn];                          // 记录路径
12 int v;
13 int N, M, S, D;
14 int tn[maxn], num[maxn], ts[maxn];      // tn: team num 城市救援队数
15                                         // num 起点到终点,最近走法拥有的路条数
16                                         // ts: team sum 能够找到的救援队数
17                                         
18 // N 城市数 M 快速道路数 S出发点 D目的点
19 void Disjkstra(int s);
20 void printPath(int Des);
21 
22 int main(){
23     // freopen("data.in", "r", stdin);
24     cin>>N >>M >>S >>D;
25     for(int i = 0; i < N; ++i) cin>>tn[i];
26     
27     memset(cost, 0x3f, sizeof(cost));
28     memset(d, 0x3f3f, sizeof(d));
29     memset(num, 0, sizeof(num));
30     memset(pre, -1, sizeof(pre));
31     while(M--){
32         int c1, c2, dist;
33         cin>>c1 >>c2 >>dist;
34         cost[c1][c2] = cost[c2][c1] = dist;
35     }
36 
37     for(int i = 0; i < N; ++i){         // 系列初始化
38         if(cost[S][i] != maxValue){
39             d[i] = cost[S][i];
40             ts[i] = tn[S] + tn[i];
41             num[i] = 1;
42             pre[i] = S;
43         }
44     }
45     Disjkstra(S);
46     cout<<num[D] <<" " <<ts[D] <<endl;
47     printPath(D);
48     return 0;
49 }
main函数
 1 void printPath(int Des){
 2     stack<int> ps;
 3     int cur = Des;
 4     while(cur != -1){
 5         ps.push(cur);
 6         cur = pre[cur];
 7     }
 8     int sz = (int)ps.size();
 9     for(int i = 0; i < sz; ++i){
10         cout<<ps.top();
11         ps.pop();
12         if(i != sz - 1){
13             cout<<" ";
14         }
15     }
16 }
打印输出函数
 1 void Disjkstra(int s){
 2     memset(used, 0, sizeof(used));  3 d[s] = 0;  4 ts[s] = tn[s];  5 num[s] = 1; // 与自己相关的一些数据  6  7 used[s] = 1; // 源点放入选中集合  8  9 for(int i = 0; i < N - 1; ++i){ // 添加 n - 1次点即可 10 int u = s; 11 int minD = maxValue; 12 for(int j = 0; j < N; ++j) 13 if(!used[j] && d[j] < minD){ 14 u = j; 15 minD = d[j]; 16  } 17 18 used[u] = 1; // u放入集合之中 19 for(int k = 0; k < N; ++k){ 20 if(!used[k] && cost[u][k] < maxValue){ 21 if(d[u] + cost[u][k] < d[k]){ 22 d[k] = cost[u][k] + d[u]; 23 pre[k] = u; 24 25 num[k] = num[u]; 26 ts[k] = ts[u] + tn[k]; 27  } 28 else if(d[u] + cost[u][k] == d[k]){ 29 num[k] += num[u]; 30 if(ts[u] + tn[k] > ts[k]){ 31 ts[k] = ts[u] + tn[k]; 32 pre[k] = u; 33  } 34  } 35  } 36  } 37  } 38 }

转载于:https://www.cnblogs.com/Rosebud/p/7188531.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值