1003. Emergency (25)

As an emergency rescue team leader of a city, you are given a special map of your country. The map shows several scattered cities connected by some roads. Amount of rescue teams in each city and the length of each road between any pair of cities are marked on the map. When there is an emergency call to you from some other city, your job is to lead your men to the place as quickly as possible, and at the mean time, call up as many hands on the way as possible.

Input

Each input file contains one test case. For each test case, the first line contains 4 positive integers: N (<= 500) - the number of cities (and the cities are numbered from 0 to N-1), M - the number of roads, C1 and C2 - the cities that you are currently in and that you must save, respectively. The next line contains N integers, where the i-th integer is the number of rescue teams in the i-th city. Then M lines follow, each describes a road with three integers c1, c2 and L, which are the pair of cities connected by a road and the length of that road, respectively. It is guaranteed that there exists at least one path from C1 to C2.

Output

For each test case, print in one line two numbers: the number of different shortest paths between C1 and C2, and the maximum amount of rescue teams you can possibly gather.
All the numbers in a line must be separated by exactly one space, and there is no extra space allowed at the end of a line.

Sample Input
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
Sample Output
2 4
此题是对Dijkstra算法的一种变式应用,在求新的路径时,考虑到要是找到了新的最短路,那么最短路径的个数应该就是前驱的最短路径个数。如果长度和原有的相等,那么个数为能达到此点的路径数为此点的路径数加上前驱结点的路径数 。

#include<cstdio>
//#define LOCAL
#include<iostream>
#define INF 100000

using namespace std;
int way[500][500];
//      这里接收了六个形参,不太好看 
int Dijkstra(int M,int S,int* D,int* Count,int* MaxTeam,int* WEIGHT);

int main(){

#ifdef LOCAL
    freopen("data.in","r",stdin);
    freopen("data.out","w",stdout);
#endif

    int M,N,S,D;
    cin >> M >> N >> S >> D;
    int MaxTeam[M],WEIGHT[M];
    int i,j,a,b,c;
//  初始化 
    for(i = 0;i<M;i++){
        cin >> MaxTeam[i];
        WEIGHT[i] = MaxTeam[i]; 
    }

    for(i = 0;i<M;i++)
        for(j = 0;j<M;j++)
            way[i][j] = INF;

    for(i = 0;i<N;i++){
        cin >> a >> b >> c;
        way[a][b] = way[b][a] = c;
    }

    int MinDist[M];
    int Count[M];

    for(i = 0;i<M;i++){
        Count[i] = 0;
        MinDist[i] = INF;
    }

    Dijkstra(M,S,MinDist,Count,MaxTeam,WEIGHT);

    cout << Count[D] << " " << MaxTeam[D];

    return 0;
}

int Dijkstra(int M,int S,int* D,int* Count,int* MaxTeam,int* WEIGHT){
    int visited[M],i;   
//  1.初始化 
    for(i = 0;i<M;i++)
        visited[i] = 0;
    for(i = 0;i<M;i++){
        D[i] = way[S][i];
        if(D[i] < INF){
            Count[i] = 1;
            MaxTeam[i] += WEIGHT[S];
        }
    }
    Count[S] = 1;
    visited[S] = 1;
    D[S] = 0;
    int j;
//  主循环开始,由于在D[]排除S本身的情况,所以从1开始循环 
    for(j = 1;j<M;j++){
//      2.查找最小值,若数据太大,可以用最小堆来存储 
        int Dmin = INF,Smin = INF;
        for(i = 0;i<M;i++){
            if(D[i]<Dmin && !visited[i]){
                Dmin = D[i];
                Smin = i;
            }
        }
//      标记访问点,若没有比INF小的则说明图不连通,返回-1 
        if(Dmin < INF)
            visited[Smin] = 1;
        else
            return -1;
//      3.开始更新一些列点 
        for(i = 0;i<M;i++)
            if(!visited[i] ){
//              若新路径比老路径小 
                if(Dmin + way[Smin][i] < D[i]){
                    D[i] = Dmin + way[Smin][i];                 //用新的路径取代老的路径 
                    Count[i] = Count[Smin];                     //可以达到此点的路径数为能达到前驱结点的个数 
                    MaxTeam[i] = MaxTeam[Smin] + WEIGHT[i];     //积累的团队为前驱结点已经汇集的团队加上此点本身的 
                }
//              若新路径和老路径相等 
                else if(Dmin + way[Smin][i] == D[i]){
                    Count[i] += Count[Smin];                    //能达到此点的路径数为此点的路径数加上前驱结点的路径数 
                    if(MaxTeam[i] - WEIGHT[i] < MaxTeam[Smin] ) //若新路径上能得到的救援队更多,则更新为新的救援队 
                        MaxTeam[i] = MaxTeam[Smin] + WEIGHT[i];
                }
            }
    }
//  程序正常结束,返回0 
    return 0; 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值