[jzoj1163]第k短路

Description

Bessie 来到一个小农场,有时她想回老家看看她的一位好友。她不想太早地回到老家,因为她喜欢途中的美丽风景。她决定选择K短路径,而不是最短路径。
农村有 R (1≤R≤100,000) 条单向的路,每条路连接 N (1≤N≤10000) 个结点中的两个。结点的编号是 1..N。Bessie 从结点 1出发,她的朋友(目的地)在结点 N。
同一个点可以多次经过。K短路的定义:假设从1出发,有M条长度不同的路径可以到达点N,则K短路就是这M条路径中第K小的路径长度。

Input

Line 1: 三个用空格分隔的整数 N,R,K((1≤n≤10000,1≤R≤100000,1≤K≤10000)
Lines 2..R+1: 每行包含三个用空格分隔的整数x,y,len(1≤x,y≤n,1≤len≤10000),表示x到y有一条长度为len的单向道路。

Output

输出包括一行,一个整数,第K短路的长度。

Sample Input

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

Sample Output

450

题解

首先,此题数据有问题,能拿80的话就说明算法对了,然后看看我的输入,改一下就过了。
然后就是关于第k短路的算法。
这一篇blog讲的比较好:
http://blog.csdn.net/z_mendez/article/details/47057461
然后说一下我的理解:
预处理出来每个点到终点的最短路,然后从起点开始A*,每次取f值最大的点,将它周围的点都更新值放到堆里,每一次搜到终点记一次数,当计数达到k次时就是答案了。
为什么这么做是对的呢?在我看来,这样就相当于在枚举起点和终点之间的点,因为是用堆存储的,所以效率较高,这也是一个搜索的过程,只不过它可以“判断”下一步走什么更容易到达下一条路。
然后还有就是看清题意,比如这个题,它对第k短路的定义就是所有长度不相等的路排序之后的第k个,所以我们要判重,这里判重用map就可以了。
代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<queue>
#include<map>
#define ll long long
using namespace std;
inline int read(){
//  int x=0;char ch=' ';int f=1;
//  while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
//  if(ch=='-')f=-1,ch=getchar();
//  while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
//  return x*f;
int x;
scanf("%d",&x);
return x;
}
struct edge{
    int to,next,w;
}e[100001],e2[100001];
int n,m,k,cnt,tot,tot2;
int head[10001];
int head2[10001];
inline void addedge(int x,int y,int l){
    e[++tot].to=y;e[tot].next=head[x];e[tot].w=l;head[x]=tot;
}
inline void addedge2(int x,int y,int l){
    e2[++tot2].to=y;e2[tot2].next=head2[x];e2[tot2].w=l;head2[x]=tot2;
}
int dis[10001];
queue<int> q1;
void spfa(){
    memset(dis,0x3f,sizeof(dis));
    q1.push(n);
    dis[n]=0;
    while(!q1.empty()){
        int x=q1.front();
        q1.pop();
        for(int i=head2[x];i;i=e2[i].next){
            int u=e2[i].to;
            if(dis[u]>dis[x]+e2[i].w){
                dis[u]=dis[x]+e2[i].w;
                q1.push(u);
            }
        }
    }
}
struct node{
    int id,f,g,h;
    node(int _id,int _f,int _g,int _h):id(_id),f(_f),g(_g),h(_h){}
    inline bool operator > (const node& y) const {
        return (f==y.f)?g>y.g:f>y.f;
    }
};
priority_queue<node,vector<node>,greater<node> > q;
map<int,int> mp;
int ans;
void Astar(){
    q.push(node(1,dis[1],0,dis[1]));
    while(!q.empty()){
        node now=q.top();
        q.pop();
        if(now.id==n){
            if(!mp[now.g]){
                mp[now.g]=1;
                cnt++;
            }
        }
        if(cnt==k){
            ans=now.g;
            return;
        }
        for(int i=head[now.id];i;i=e[i].next){
            int u=e[i].to;
            q.push(node(u,now.g+e[i].w+dis[u],now.g+e[i].w,dis[u]));
        }
    }
}
int main(){
    n=read();m=read();k=read();
    for(int i=1;i<=m;i++){
        int x=read(),y=read(),l=read();
        while(getchar()!='\n');
        addedge(x,y,l);addedge2(y,x,l);
    }
    spfa();
    Astar();
    printf("%d",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值