【最短路】HDU - 6705 - path

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


题意

给出一张图,每条边可以走任意多次,问所有路径的第k短。


题解

介绍一个乱做的方法。
首先如果直接跑最短路会MLE(以及TLE),但很明显跑最短路的时候很多权值太大的是不需要的。
所以我就二分了一个权值mid,如果跑出来的权值大于mid,我就不要。这样如果最短路队列里面有k个,就把二分的上界缩小,否则下界变大。
二分出界限后,再跑最短路就行了。
这样复杂度是 n l o g 2 n nlog^2n nlog2n的,二分一个 l o g log log,最短路一个 l o g log log,会TLE。
但观察一下就可以发现,二分的判断其实不需要用到优先队列,只用普通的队列就行了,因为它只需要判断是否有k个。
这样复杂度就降成了 n l o g nlog nlog级别。
最后加个剪枝,给边排个序,如果已经大于了就break掉,这样就可以过了。

正常做法应该是用multiset维护大小为k的堆,每次从后面删。


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+7;
const ll inf=1e14;
int T,n,m,qn;
struct Edge{
    int v,w;
    bool operator<(const Edge k)const{
        return w<k.w;
    }
};
vector<Edge>e[N];
int qq[N];
ll ans[N];
struct Node{
    int pos;
    ll val;
    bool operator<(const Node k)const{
        return val>k.val;
    }
};

int mx;
queue<Node>q;
inline bool ck(const ll mid){
    while(!q.empty()) q.pop();
    for(int i=1;i<=n;i++){q.push((Node){i,0});}
    int cnt=0;
    while(!q.empty()){
        Node t=q.front();q.pop();
        int u=t.pos;
        for(int i=0;i<e[u].size();i++){
            Edge vv=e[u][i];
            if(0ll+t.val+vv.w>mid) break;
            cnt++;
            if(cnt>=mx) return true;
            q.push((Node){vv.v,0ll+t.val+vv.w});
        }
    }
    return false;
}
priority_queue<Node>qqq;
inline int dij(const ll mid){
    int tot=0;
    while(!qqq.empty()) qqq.pop();
    for(int i=1;i<=n;i++){qqq.push((Node){i,0});}
    while(!qqq.empty()){
        Node t=qqq.top();qqq.pop();
        int u=t.pos;
        if(t.val>0ll) ans[++tot]=0ll+t.val;
        if(tot>=mx) break;
        for(int i=0;i<e[u].size();i++){
            Edge vv=e[u][i];
            if(0ll+t.val+vv.w>mid) break;
            qqq.push((Node){vv.v,0ll+t.val+vv.w});
        }
    }
    return false;
}
int main()
{
   // freopen("in.in","r",stdin);
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%d",&n,&m,&qn);
        for(int i=1;i<=n;i++) e[i].clear();
        for(int i=1,u,v,w;i<=m;i++){
            scanf("%d%d%d",&u,&v,&w);
            e[u].push_back((Edge){v,w});
        }
        for(int i=1;i<=n;i++){
            sort(e[i].begin(),e[i].end());
        }
        mx=0;
        for(int i=1;i<=qn;i++){
            scanf("%d",&qq[i]);
            mx=max(mx,qq[i]);
        }
        ll lo=0,hi=inf,nd=0;
        while(lo<=hi){
            ll mid=lo+hi>>1;
            if(ck(mid)) nd=mid,hi=mid-1;
            else lo=mid+1;
        }
        dij(nd);
        for(int i=1;i<=qn;i++){
            printf("%lld\n",ans[qq[i]]);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值