poj 3255

题目概述

有N个地点,R条双向路,求次短路长度

时限

2000ms/6000ms

输入

第一行整数N,R,其后R行,每行三个整数,描述一条路连接的两个地点及其长度,输入只有一组

限制

1<=N<=5000;1<=R<=100000

输出

一个数,为所求次短路长度

样例输入

4 4
1 2 100
2 4 200
2 3 250
3 4 100

样例输出

450

讨论

图论,第K短路,bellman_ford队列优化+A*算法,前面在poj 2449用过一次,这里相当于简化了一下,这里的A*是基于一个基本公式求得次短路的,f=g+h,h是从终点到某个点a的最短路,g是从起点到点a的最短路,这样f的意义就是从起点经过点a到终点的最短路,这样的路有不少,将所有这样的路按f的大小升序排列,第一条就是平日里常见的最短路,第二条就是题目要求的次短路
从实现方面说,首先将所有路的方向反转(当然这个题本来就是双向路),利用bellman_ford算出终点到所有点的最短距离,也就是h,然后A*从起点开始,把和遇到的点相连的点都加入优先队,利用优先队对f进行排序,第一次遇到终点得到最短路,第二次就是次短路了,然后输出f(其实g也一样,毕竟终点到终点距离是0,h是0)

题解状态

2644K,172MS,C++,1389B

题解代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
#define INF 0x3f3f3f3f
#define MAXN 5003
#define memset0(a) memset(a,0,sizeof(a))

int N, R;//地点总数 路径总数
int to[200003], w[200003], pre[200003], al[MAXN], index = 1, dis[MAXN];//邻接表的4数组1变量 to 路的终点 weight 权值 previous 同起点上一条路的下标 adjacency_list 每个起点的最后一条路下标 index 分配路下标的变量 前面的数组大小是因为100000*2 双向路要双倍 distance 最短路长度
queue<int>q;
bool inq[MAXN];
struct It//A*的路的结构
{
    int to, g;//某个中间节点 起点到点to的最短距离
    bool operator<(const It &b)const
    {
        return g + dis[to]>b.g + dis[b.to];//按f降序排列 放到优先队 队头是f最小的
    }
};
priority_queue<It>pq;//A*的优先队
int fun()
{
    for (int p = 0; p < R; p++) {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);//input
        to[index] = b, w[index] = c, pre[index] = al[a], al[a] = index++;
        to[index] = a, w[index] = c, pre[index] = al[b], al[b] = index++;
    }
    for (int p = 1; p <= N; p++)//下面是bellman_ford 从N开始反向求最短路长度 即每个点的h
        dis[p] = INF;
    dis[N] = 0;
    q.push(N);
    while (!q.empty()) {
        int a = q.front();
        q.pop();
        inq[a] = 0;
        for (int p = al[a]; p; p = pre[p]) {
            if (dis[to[p]]>dis[a] + w[p]) {
                dis[to[p]] = dis[a] + w[p];
                if (!inq[to[p]]) {
                    q.push(to[p]);
                    inq[to[p]] = 1;
                }
            }
        }
    }
    int cnt = 0;//已经遇到终点的次数 当第二次遇到时就是次短路
    It a, b;//为避免每次都构造而提前声明
    a.to = 1, a.g = 0;
    pq.push(a);//压入起点
    while (!pq.empty()) {
        a = pq.top();
        pq.pop();
        if (a.to == N)//当遇到终点时
            if (++cnt > 1 && a.g > dis[1])//第二个判断是从poj 2449搬来的 可能并无实际意义 只是防止起点就是终点
                return a.g;
        for (int p = al[a.to]; p; p = pre[p]) {
            b.to = to[p], b.g = a.g + w[p];//计算出邻接点的属性
            pq.push(b);//无论长短都压入优先队
        }
    }
}
int main(void)
{
    //freopen("vs_cin.txt", "r", stdin);
    //freopen("vs_cout.txt", "w", stdout);

    scanf("%d%d", &N, &R);//input
    printf("%d\n", fun());//output
}

EOF

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值