5-35 城市间紧急救援 (25分)

5-35 城市间紧急救援 (25分)

作为一个城市的应急救援队伍的负责人,你有一张特殊的全国地图。在地图上显示有多个分散的城市和一些连接城市的快速道路。每个城市的救援队数量和每一条连接两个城市的快速道路长度都标在地图上。当其他城市有紧急求助电话给你的时候,你的任务是带领你的救援队尽快赶往事发地,同时,一路上召集尽可能多的救援队。
输入格式:

输入第一行给出4个正整数N、M、S、D,其中N(2≤N≤500)是城市的个数,顺便假设城市的编号为0 ~ (N−1);M是快速道路的条数;S是出发地的城市编号;D是目的地的城市编号。

第二行给出N个正整数,其中第i个数是第i个城市的救援队的数目,数字间以空格分隔。随后的M行中,每行给出一条快速道路的信息,分别是:城市1、城市2、快速道路的长度,中间用空格分开,数字均为整数且不超过500。输入保证救援可行且最优解唯一。
输出格式:

第一行输出最短路径的长度和和能够召集的最多的救援队数量。第二行输出从SSS到DDD的路径中经过的城市编号。数字间以空格分隔,输出结尾不能有多余空格。
输入样例:

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

思路
关键词:确定起点终点单源最短路 无向图
Dijkstra算法就是生而为了解决这种题目的。不过这道题需要我们找出所有最短路的数量,那就必须在原来的标准代码上做点修改,不能找到终点就跳出Dijkstra函数,而是要完全遍历全图。
后面递归输出路径的时候则是利用了先前Dijkstra函数中生成使用的两组数据:从起点S到各结点的最短路径长度,从起点S到各结点的救援队数量;利用这个数据,我们从终点开始遍历他的所有边,若存在某条边DA连接终点D和某结点A使得“从起点S到A能获得的最大救援队数量+D点的救援队数量==从起点S到D能获得的最大救援队数量”&&“从起点S到A的最短路径长度+边DA的长度==从起点S到D的最短路径长度”那么这个结点A就必然是起点S到终点D最短路径最多救援队路径上最靠近终点D的那个驿站。

点击访问 PTA-测验

#include <stdio.h>
#include<stdlib.h>
#define FULL 250001
/*
提交时间    状态  分数  题目  编译器 耗时  用户
2018/1/23 11:57:04  答案正确    25  7-35    C (gcc) 238 ms  569985011
测试点 提示  结果  耗时  内存
0   sample  答案正确    2 ms    240KB
1   5条不同的最短路    答案正确    2 ms    244KB
2   最小N和M   答案正确    2 ms    156KB
3   最大N和M,随机数据构成完全图 答案正确    238 ms  2196KB
*/
typedef struct node *Node;
typedef struct {
    int Node;//目标结点
    int Lenth;//边长
} data;
struct node {
    data Reached[501];//与之有边连接的结点集合
    int right;//集合里的结点个数
};

int teams[501];//保存每个结点带有的救援队数目
int Lenth[501][3];//[0]记录从起点出发到各结点的路径长度 ;[1]记录该路径能获取的救援队总数;【2】记录等长路径条数
Node Map;//地图

int cmp(const void*a,const void*b) {//
    data*x=(data*)a;
    data*y=(data*)b;
    if(x->Lenth!=y->Lenth) {
        return x->Lenth-y->Lenth;
    } else if(teams[x->Node]!=teams[y->Node]) {
        return teams[y->Node]-teams[x->Node];
    } else  return teams[x->Node]-teams[y->Node];
}

void Dijkstra(int);
void DFS(int,const int);
int main() {
    int N,M,S,D;//结点数、边数、起点、终点
    scanf("%d%d%d%d",&N,&M,&S,&D);

    Map=(Node)malloc(sizeof(struct node)*N);//初始化地图上的结点
    for(int i=0; i<N; Map[i++].right=0);

    for(int i=0; i<N; scanf("%d",&teams[i++]));//读入每个结点的价值——城市拥有的救援队数量

    {
        //读入地图信息,并排序整理
        while(M--) {
            int a,b,lenth;
            scanf("%d%d%d",&a,&b,&lenth);
            Map[a].Reached[Map[a].right].Node=b;
            Map[a].Reached[Map[a].right++].Lenth=lenth;
            Map[b].Reached[Map[b].right].Node=a;
            Map[b].Reached[Map[b].right++].Lenth =lenth;
        }
        for(int i=0; i<N; i++) {
//          printf("\n%d:",i);
            qsort(Map[i].Reached,Map[i].right,sizeof(data),cmp);
            for(int j=0; j<Map[i].right; j++) {
//              printf("{%d-%d}",Map[i].Reached[j].Node,Map[i].Reached[j].Lenth);
            }
        }
    }

    for(int i=0; i<N; Lenth[i++][0]=FULL); //[0]初始化设定从起点到所有其他结点的路径长度为无穷;
    Lenth[S][0]=0;
    Lenth[S][1]=teams[S];
    Dijkstra(S);
    printf("%d %d\n",Lenth[D][2],Lenth[D][1]);
    DFS(D,S);
    printf("%d",D);
    return 0;
}


void Dijkstra(int a) {//戴克斯特拉算法

    for(int i=0; i<Map[a].right; i++) {
        data temp=Map[a].Reached[i];
        if(Lenth[a][0]+temp.Lenth<Lenth[temp.Node][0]) {
            Lenth[temp.Node][0]=Lenth[a][0]+temp.Lenth;
            Lenth[temp.Node][1]=Lenth[a][1]+teams[temp.Node];
            Lenth[temp.Node][2]=1;
            Dijkstra(temp.Node);
        } else if(Lenth[a][0]+temp.Lenth==Lenth[temp.Node][0]) {
            if(Lenth[a][1]+teams[temp.Node]>Lenth[temp.Node][1]) {
                Lenth[temp.Node][1]=Lenth[a][1]+teams[temp.Node];
            }
            ++Lenth[temp.Node][2];
            Dijkstra(temp.Node);
        }
    }

}

void DFS(int dd,const int S) {//深度优先,从终点递归回溯, 如果存在某个结点:它到终点的边长加上它到起点的长度== 终点到原点的长度&&它到起点的救援队数量+终点的救援队数量== 起点到终点的救援队数量,那么这个结点一定是最短路上的一个结点。
    if(dd==S)return;
    for(int i=0; i<Map[dd].right; i++) {
        data temp=Map[dd].Reached[i];
        if(Lenth[temp.Node][0]+temp.Lenth==Lenth[dd][0]&&
                Lenth[temp.Node][1]+teams[dd]==Lenth[dd][1]) {
//          printf("*");
            DFS(temp.Node,S);
            printf("%d ",temp.Node);
        }
    }
}
  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值