poj 2449

来源:互联网 发布:2016网络作家富豪榜 女 编辑:程序博客网 时间:2024/06/05 09:19

题目概述

有N个地点,编号1到N,地点之间有M条单向路,要沿着第K短路从S到T,问其用时

时限

4000ms/12000ms

输入

第一行整数N,其后N行,每行三个整数a,b,c,描述一条路的起点,终点,途经用时,最后一行三个整数S,T,K,输入只有一组

限制

1<=N<=1000;1<=M<=100000;1<=c<=100;1<=K<=1000

输出

一个数,所求用时,若不存在第K短路则为-1

样例输入

2 2
1 2 5
2 1 4
1 2 2

样例输出

14

讨论

图论,第K短路,bellman_ford队列优化+A*,先将图中边反转,构图求最短路,这是从T到其他点的最短距离,然后A*,A*需要利用一个公式,f=g+h,这里h就是刚求的T到其他某点最短路长,g是从S到同一个点最短路长,f的意义自然就是从S经过某点到T的最短路长,显然经过的这个某个点不一定是最短路上的点,因而这条路也不一定最短路,但一定是经过这个点的最短路,那这就好办了,把所有的f都排序,去掉最短的,下一个就是次短,再去,就是第三短,最终总能找到第K短的,由于不知道到底哪些点在最短路上,因而选定这个某个点为终点T就好了,终点肯定在最短路上,第K次遇到这个某个点为终点的路,这条路就是第K短路,为什么不选起点?因为f描述的就是从起点S出发的,起点和终点都是自己,这不符合条件
从实现层面上,首先需要一个优先队列完成上述排序工作,每次弹出一个点,然后将其连接的点计算出f,之后塞到队列中,另外需要注意优先队列每次弹出的是最大的,因而重载小于号时需要留意,重载方向错也能过样例,但只会是MLE,因为会无限入队,另外有个坑,起点就是终点的时候不能算,可惜额没有找到原题中这句话在哪里
题目的意思到底是什么已经不重要了,可能是双向路,也可能是从T到S,但是都一样能做
实际上题解并不是最快的一次,但写的比较规整一点

题解状态

7476K,266MS,C++,1599B

题解代码

#include<cstdio>#include<cstring>#include<algorithm>#include<queue>using namespace std;#define INF 0x3f3f3f3f#define MAXN 1003#define memset0(a) memset(a,0,sizeof(a))int N, M, S, T, K, index, dis[MAXN];//地点数 路数 起点编号 终点编号 第K短路 index 为邻接表提供下标 distance 也就是公式中的hint to1[MAXN * 100], w1[MAXN * 100], pre1[MAXN * 100], al1[MAXN];//正方向图int to2[MAXN * 100], w2[MAXN * 100], pre2[MAXN * 100], al2[MAXN];//反方向图struct It//A*中用到的路的结构{    int to, g;//路的终点 从S到to的最短路长    bool operator<(const It &b)const    {        return g + dis[to] > b.g + dis[b.to];//之前由于这个写反了贡献一排MLE    }};priority_queue<It>pq;//A*的优先队queue<int>q;//bellman_ford的队bool inq[MAXN];//bellman_ford的辅助数组 在队中int fun(){    index = 1;    for (int p = 0; p < M; p++) {        int a, b, c;        scanf("%d%d%d", &a, &b, &c);//input        to1[index] = b, w1[index] = c, pre1[index] = al1[a], al1[a] = index;        to2[index] = a, w2[index] = c, pre2[index] = al2[b], al2[b] = index++;//这是两张图 因而下标一样也无所谓    }    scanf("%d%d%d", &S, &T, &K);//input    for (int p = 1; p <= N; p++)//下面是bellman_ford算法        dis[p] = INF;    dis[T] = 0;    q.push(T);    while (!q.empty()) {        int a = q.front();        q.pop();        inq[a] = 0;        for (int p = al2[a]; p; p = pre2[p]) {            if (dis[to2[p]] > dis[a] + w2[p]) {                dis[to2[p]] = dis[a] + w2[p];                if (!inq[to2[p]]) {                    q.push(to2[p]);                    inq[to2[p]] = 1;                }            }        }    }    It a, b;//下面是A*算法    int cnt = 0;    if (S == T)//起点就是终点时要额外数一条路        K++;    if (dis[S] == INF)//如果bellman_ford已经认定没有路就不用继续了        return -1;    a.to = S, a.g = 0;    pq.push(a);    while (!pq.empty()) {        a = pq.top();        pq.pop();        if (a.to == T)            cnt++;        if (cnt == K)            return a.g;        for (int p = al1[a.to]; p; p = pre1[p]) {            b.to = to1[p], b.g = a.g + w1[p];            pq.push(b);        }    }    return -1;//这行似乎是多余的 但是没有试过}int main(void){    //freopen("vs_cin.txt", "r", stdin);    //freopen("vs_cout.txt", "w", stdout);    scanf("%d%d", &N, &M);//input    printf("%d\n", fun());//output}

EOF

0 0