A*(A - star) - Remmarguts’ Date - POJ - 2449

A*(A - star) - Remmarguts’ Date - POJ - 2449

题意:

给定一张N个点(编号1,2…N),M条边的有向图,求从起点S到终点T的第K短路的长度,路径允许重复经过点或边。

注意: 每条最短路中至少要包含一条边。

输入格式
第一行包含两个整数N和M。

接下来M行,每行包含三个整数A,B和L,表示点A与点B之间存在有向边,且边长为L。

最后一行包含三个整数S,T和K,分别表示起点S,终点T和第K短路。

输出格式
输出占一行,包含一个整数,表示第K短路的长度,如果第K短路不存在,则输出“-1”。

数据范围
1 ≤ S , T ≤ N ≤ 1000 , 0 ≤ M ≤ 1 0 5 , 1 ≤ K ≤ 1000 , 1 ≤ L ≤ 100 1≤S,T≤N≤1000, 0≤M≤10^5, 1≤K≤1000, 1≤L≤100 1S,TN1000,0M105,1K1000,1L100
输入样例:

2 2
1 2 5
2 1 4
1 2 2

输出样例:

14

分析:

要 求 从 S 到 T 的 第 k 短 路 径 。 要求从S到T的第k短路径。 STk

对 于 每 个 点 u , 我 们 计 算 从 起 点 S 到 u 的 距 离 d [ u ] , 设 从 u 到 终 点 T 的 最 短 距 离 为 d i s t [ u ] , 对于每个点u,我们计算从起点S到u的距离d[u],设从u到终点T的最短距离为dist[u], uSud[u]uTdist[u]

以 d [ u ] + d i s t [ u ] 为 第 一 关 键 字 , 用 小 根 堆 来 扩 展 。 以d[u]+dist[u]为第一关键字,用小根堆来扩展。 d[u]+dist[u]

于 是 启 发 函 数 通 过 d i j k s t r a 实 现 , 求 源 点 为 T 的 最 短 路 径 , 存 入 d i s t 数 组 。 于是启发函数通过dijkstra实现,求源点为T的最短路径,存入dist数组。 dijkstraTdist

当 终 点 T 第 k 次 出 队 , 此 时 的 路 径 长 度 为 第 k 短 路 。 当终点T第k次出队,此时的路径长度为第k短路。 Tkk

具体落实:

① 、 反 向 建 图 , 以 T 为 源 点 跑 一 遍 d i j k s t r a , 得 到 d i s t 估 价 函 数 。 ①、反向建图,以T为源点跑一遍dijkstra,得到dist估价函数。 Tdijkstradist

② 、 正 向 建 图 , a s t a r 算 法 得 到 第 k 短 路 。 ②、正向建图,astar算法得到第k短路。 astark

注意:

本 题 要 求 每 条 路 径 至 少 包 含 一 条 边 , 当 起 点 与 终 点 相 同 时 , 必 须 要 经 过 其 他 点 再 回 到 起 点 才 算 一 条 路 径 。 本题要求每条路径至少包含一条边,当起点与终点相同时,必须要经过其他点再回到起点才算一条路径。

这 种 情 况 将 k 自 增 1 , 排 除 掉 距 离 为 0 的 最 短 路 径 的 情 况 。 这种情况将k自增1,排除掉距离为0的最短路径的情况。 k10

代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>

#define P pair<int,int>
#define Pi pair<int,P>
#define x first
#define y second

using namespace std;

const int N=1010,M=2e5+10;

int n,m,k,S,T;
bool st[N];
int e[M],ne[M],h[N],rh[N],w[M],idx;
int dist[N];

void add(int h[],int a,int b,int c)
{
    e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}

int dijkstra()
{
    memset(dist,0x3f,sizeof dist);
    dist[T]=0;

    priority_queue<P,vector<P>,greater<P> > heap;
    heap.push({0,T});

    while(heap.size())
    {
        P t=heap.top();
        heap.pop();

        int ver=t.y;
        if(st[ver]) continue;
        st[ver]=true;

        for(int i=rh[ver];~i;i=ne[i])
        {
            int j=e[i];
            if(dist[j]>dist[ver]+w[i])
            {
                dist[j]=dist[ver]+w[i];
                heap.push({dist[j],j});
            }
        }
    }
}

int astar()
{
    priority_queue<Pi,vector<Pi>,greater<Pi> > heap;
    heap.push({dist[S],{0,S}});		//1.估计最短距离下限 2.已经走过的距离 3.节点编号

    int cnt=0;
    while(heap.size())
    {
        Pi t=heap.top();
        heap.pop();

        int ver=t.y.y,distance=t.y.x;
        if(ver==T) cnt++;
        if(cnt==k) return distance;

        for(int i=h[ver];~i;i=ne[i])
        {
            int j=e[i];
            heap.push({distance+w[i]+dist[j],{distance+w[i],j} });
        }
    }

    return -1;
}

int main()
{
    memset(h,-1,sizeof h);
    memset(rh,-1,sizeof rh);

    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        add(h,a,b,c);
        add(rh,b,a,c);
    }

    scanf("%d%d%d",&S,&T,&k);
    if(S==T) k++;  //

    dijkstra();
    printf("%d\n",astar());

    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值