HDU6705.Path(任意起点终点的k短路)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6705

题目大意:

给你一个有向图,任意一个点和边都可以经过很多次,问你整个图中,任意起点终点的k短路的长度是多少?你需要回答q个询问,每个询问给一个k

思路:

优先队列优化搜索,每次将所有点都加入队列中,意即每个点都可以为起点。那么,如果我们真正暴力地去搜索整个图,也就是每搜到一个点就把它所有相邻的点都加入优先队列中,那很明显会喜提TLE,只要将图变得很稠密,算法就会变为O(n^2)的规模。

这里有一个比较好想的优化,对每个点出度的边按照权值从小到大排序,如果我们每次按照最小的边扩张,那就是求最短路的方式。

那么k短路的情况有哪些呢?假设当前遍历的边为e,之前的点为e.from,现在的点为e.to,那么有两种情况可能更新答案:

第一,从e.from这个点出发,不经过e,而是通过from的另一条出边,这样有可能更新答案。

第二,从e.from出发,经过了e,到达了点e.to,选择e.to的一条出边,这样有可能更新答案。

所以,对于访问的一个点,只会有一条边进入优先队列中,再暴力地找就可以实现log的复杂度了。

在具体实现的时候,优先队列维护条边当前的出点,和这条边的编号,这样可以更方便的更新答案。

#pragma GCC optimize ("O2")
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,int> pli;
const int MAXV=5e4+5,MAXE=5e4+5;
int V,E,Q;
ll ans[MAXV];
pii query[MAXV];

namespace G
{
    struct Edge
    {
        int to, last, l;
        void set(int _to, int _last, int _l) { to = _to, last = _last, l = _l; }
    } es[MAXE]; // 边记得开两倍!!!

    int top, head[MAXV];

    void init() { top = 0, fill(head + 1, head + V + 1, -1); }
    inline void add(int fr, int to, int l)
    {
        es[top].set(to, head[fr], l);
        head[fr] = top++;
    }
    void sort_edge()
    {
        priority_queue<pii,vector<pii>,greater<pii> > q;
        for(int i=1;i<=V;++i)
        {
            for(int j=head[i];~j;j=es[j].last) q.push(pii(es[j].l,es[j].to));
            for(int j=head[i];~j;j=es[j].last) tie(es[j].l,es[j].to)=q.top(),q.pop();
        }
    }
    void work()
    {
        priority_queue<pli,vector<pli>,greater<pli>> q;
        for(int i=1;i<=V;++i)
            if(~head[i]) q.push(pli(es[head[i]].l,head[i]));
        int pos1=0,pos2=0,cur,last;
        ll dis;
        while(pos2<Q)
        {
            tie(dis,cur)=q.top(),q.pop();
            ++pos1;
            while(pos2<Q && pos1==query[pos2].first)
                ans[query[pos2].second]=dis,++pos2;
            last=es[cur].last;
            if(~last) q.push(pli(dis-es[cur].l+es[last].l,last)); // 加入出点相同的下一条边
            last=head[es[cur].to];
            if(~last) q.push(pli(dis+es[last].l,last)); // 加入从入点出去的第一条边
        }
    }
}

namespace io {
    const int SIZE = 1e7 + 10;
    char inbuff[SIZE];
    char *l, *r;
    inline void init() {
        l = inbuff;
        r = inbuff + fread(inbuff, 1, SIZE, stdin);
    }
    inline char gc() {
        if (l == r) init();
        return (l != r) ? *(l++) : EOF;
    }
    void read(int &x) {
        x = 0; char ch = gc();
        while(!isdigit(ch)) ch = gc();
        while(isdigit(ch)) x = x * 10 + ch - '0', ch = gc();
    }
} using io::read;

int main()
{
    // freopen(".in", "r", stdin);
    // freopen(".out", "w", stdout);
    int T;
    read(T);
    while(T--)
    {
        read(V),read(E),read(Q);
        G::init();
        int fr,to,l;
        for(int i=0;i<E;++i)
        {
            read(fr),read(to),read(l);
            G::add(fr,to,l);
        }
        G::sort_edge();
        for(int i=0;i<Q;++i)
        {
            read(query[i].first);
            query[i].second=i;
        }
        sort(query,query+Q);
        G::work();
        for(int i=0;i<Q;++i) printf("%lld\n",ans[i]);
    }
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值