第六天PAT-A1003 Emergency最短路问题Dijkstra算法(小根堆)描述及模板

9 篇文章 0 订阅
7 篇文章 0 订阅

A1003

Description:

作为一个城市的急救队领导,你会得到你所在城市的一个特别地图。这张地图展示了一些由一些路径相连的几个零散的城市。每个城市的救援队总数和连接两个城市的道路的长度都标注在地图上。当有急救电话从别的城市呼叫你的时候,你的任务就是尽快带领你的队伍到达位置,同时,尽可能多地召集路中的人手。

Input:

  • 每组测试一个样例
  • 首行给出四个正整数:城市数目N<=500,道路数M,你所在的城市C1和你必须拯救的城市C2
  • 接下来一行给出N个整数,分别是对应下标城市的救援队数目
  • 接下来M行,每行描述了一条路径

Output:

  • 每个样例输出包含两个数字的一行内容,C1C2最短路的不同条数以及你在可能路径中可以召集到的最大救援队数量。

算法思想:

  • 首先要得到最短路的距离,即解决“最短是多短”的问题,距离为正值,故可采用Dijkstra算法(注:边权为负数的图问题禁用Dijkstra);
  • 然后求不同条数,很自然想到DFS,搜索到终点时将不同条数计数器+1;
  • 由于要求路径中可以召集到的最大救援队数量,所以必须要知道最短路的路径,并在搜索到终点时将沿途各点的救援队数量求和,并保存最大值;
  • 对于DFS路径保存,可以用一个全局变量保存当前访问的点,并在DFS参数中设置一个变量表示当前记录过的城市数目。
  • 先看懂总结记忆中的两个模板,再看代码😃

总结记忆:

  1. 图的邻接表存储
const int maxm = ;
const int maxn = ;
const int inf = 0x3f3f3f3f;
struct edge{
    int dis;	//可选属性,如此时为边距离
    int v;      //边终点
    int next;   //下一路径下标
}e[maxm];
int p[maxn], eid;	//p记录每个节点的始边下标,eid记录总变数下标
void init(){
    memset(p, -1, sizeof(p));
    eid = 0;
}
void insingle(int u, int v, int dis){	//新增边
    Edge[eid].v = v;
    Edge[eid].dis = dis;
    Edge[eid].next = p[u];
    p[u] = eid++;
}
  1. 小根堆优化O((V+E)logV)
typedef pait<int, int>PII;
set<PII, less<PII>>min_heap;	//set伪实现一个小根堆
int dis[maxn];	//存储单源最短路径的结果
bool vst[maxn];	//标记每个顶点是否已扩展过(在集合U中)
bool dijkstra(int s){	//s即源点
    memset(vst, 0, sizeof(vst));
    memset(dis, 0x3f, sizeof(dis));
    min_heap.insert(make_pair(0, s));	//按 距离-下标 格式存储,按距离排序
    dis[s] = 0;
    for(int i = 0; i < n; i++){
        //小根堆无可用节点,说明存在顶点无法从源头访达,算法结束
        if(min_heap.size() == 0)	
        	return false;
        //获取堆顶元素,并将堆顶元素从堆中删除
        auto iter = min_heap.begin();
        int v = iter->second;	//获得堆顶(即最小距离)的下标
        min_heap.erase(*iter);
        vst[v] = true;
        //dijkstra算法松弛操作
        for(int j = p[v]; j != -1; j = e[j].next){
            int x = e[j].v;
            if(!vst[x] && dis[v]+e[j].dis < dis[x]){
                min_heap.erase(make_pair(dis[x], x));
                dis[x] = dis[v] + e[j].w;
                min_heap.insert(make_pair(dis[x], x));
            }
        }
    }
    return true;
}

源码:

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>
#include<algorithm>
#include<map>
#include<set>
#include<vector>
#include<queue>
using namespace std;
const int maxn = 505;
const int maxm = maxn*maxn/2;
const int inf = 0x3f3f3f3f;
struct edge{
    int dis;    //距离
    int v;      //终点
    int next;   //下一路径下标
}Edge[maxm];
typedef pair<int, int>PII;
vector<int>record[maxn];
set<PII,less<PII>>min_heap; //set伪实现一个小根堆
int p[maxn], eid;
int teamnum[maxn];
int n, m, c1, c2;
int shortest, cnt;
int pathrecord[maxn];
int dis[maxn];
int ans[2];
bool vst[maxn];
void init(){
    memset(p, -1, sizeof(p));
    eid = 0;
    shortest = inf;
    cnt = 0;
}
void insingle(int u, int v, int dis){
    Edge[eid].v = v;
    Edge[eid].dis = dis;
    Edge[eid].next = p[u];
    p[u] = eid++;
}
void in(int u, int v, int dis){
    insingle(u, v, dis);
    insingle(v, u, dis);
}
bool dijkstra(int s){
    memset(vst, false, sizeof(vst));
    memset(dis, 0x3f, sizeof(dis));
    min_heap.insert(make_pair(0, s));
    dis[s] = 0;
    record[s].push_back(s);
    for(int i = 0; i < n; i++){
        if(min_heap.size() == 0)
            return false;
        auto iter = min_heap.begin();
        int v = iter->second;
        min_heap.erase(*iter);
        vst[v] = true;
        for(int j = p[v]; j != -1; j = Edge[j].next){
            int x = Edge[j].v;
            if(!vst[x] && dis[v] + Edge[j].dis < dis[x]){
                min_heap.erase(make_pair(dis[x], x));
                record[x].insert(record[x].end(), record[v].begin(), record[v].end());
                record[x].push_back(x);
                dis[x] = dis[v] + Edge[j].dis;
                min_heap.insert(make_pair(dis[x], x));
            }
        }
    }
    return true;
}
void dfs(int now, int cnt, int index, int pre){
    pathrecord[index] = now;
    if(cnt > shortest)
        return ;
    if(now == c2){
        int totalnum = 0;
        for(int i = 0; i <= index; i++){
            totalnum += teamnum[pathrecord[i]];
        }
        if(totalnum > ans[1])
            ans[1] = totalnum;
        ans[0]++;
        return ;
    }
    for(int j = p[now]; j != -1; j = Edge[j].next){
        if(Edge[j].v != pre)
            dfs(Edge[j].v, cnt+Edge[j].dis, index+1, now);
    }
}
int main()
{
#ifdef ONLINE_JUDGE
#else
    freopen("1.txt", "r", stdin);
#endif // ONLINE_JUDGE
    scanf("%d%d%d%d", &n, &m, &c1, &c2);
    init();
    for(int i = 0; i < n; i++){
        scanf("%d", &teamnum[i]);
    }
    int u, v, d;
    for(int i = 0; i < m; i++){
        scanf("%d%d%d", &u, &v, &d);
        in(u, v, d);
    }
    dijkstra(c1);
    shortest = dis[c2];
    dfs(c1, 0, 0, -1);
    printf("%d %d\n", ans[0], ans[1]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值