蓝书(算法竞赛进阶指南)刷题记录——POJ2449 Remmarguts‘ Date(A*求解K短路)

题目:POJ2449.
题目大意:给定一张 n n n个点 m m m条边的有向图,求 s s s t t t的第 k k k短路.
1 ≤ n , k ≤ 1 0 3 , 1 ≤ m ≤ 1 0 5 1\leq n,k\leq 10^3,1\leq m\leq 10^5 1n,k103,1m105.

首先考虑直接从堆优化dijkstra拓展,容易发现堆中第 k k k个弹出的到点 x x x的路径长度一定是到点 x x x k k k短路,所以我们可以在每次加入元素是不去管三角形不等式的限制直接加入,复杂度 O ( k ( n + m ) log ⁡ ( n + m ) ) O(k(n+m)\log(n+m)) O(k(n+m)log(n+m)).

这样子做貌似有些慢,我们考虑用A*优化,在堆中用 f [ x ] [ k ] + d i s [ x ] f[x][k]+dis[x] f[x][k]+dis[x]作为比较关键字而不是 f [ x ] [ k ] f[x][k] f[x][k],其中 f [ x ] [ k ] f[x][k] f[x][k]表示当前到点 x x x的第 k k k短路, d i s [ x ] dis[x] dis[x]表示 x x x到终点的最短路长度.

这样子的话虽然复杂度上界仍然是 O ( k ( n + m ) log ⁡ ( n + m ) ) O(k(n+m)\log(n+m)) O(k(n+m)log(n+m)),但是明显跑不满,实测可以通过.

注意,这道题从起点出发不经过任何边到起点的路径不算,也就是说得特判一下终点为起点的情况,若是这种情况则输出 k + 1 k+1 k+1短路.

代码如下:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue> 
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=1000,M=100000,INF=(1<<30)-1;

int n,m,s,t,k;
struct side{
  int y,next,v;
}e[M*4+9];
int lin[2][N+9],top;

void Ins(int id,int x,int y,int v){
  e[++top].y=y;e[top].v=v;
  e[top].next=lin[id][x];
  lin[id][x]=top;
}

struct state{
  int x,v;
  state(int X=0,int V=0){x=X;v=V;}
  bool operator > (const state &p)const{return v>p.v;}
};
priority_queue<state,vector<state>,greater<state> >q;
int dis[N+9],use[N+9];

void Dijkstra(int id,int s){
  for (int i=1;i<=n;++i) dis[i]=INF,use[i]=0;
  dis[s]=0;q.push(state(s,0));
  int t;
  while (!q.empty()){
    t=q.top().x;q.pop();
    if (use[t]) continue;
    use[t]=1;
    for (int i=lin[id][t];i;i=e[i].next)
      if (dis[t]+e[i].v<dis[e[i].y]){
        dis[e[i].y]=dis[t]+e[i].v;
        q.push(state(e[i].y,dis[e[i].y]));
	  }
  }
}

int cnt[N+9];

int Astar(int id,int st,int td,int k){
  if (st==td) ++k;
  if (dis[st]==INF) return -1;
  for (int i=1;i<=n;++i) cnt[i]=0;
  q.push(state(st,dis[st]));
  int t,v;
  while (!q.empty()){
  	t=q.top().x;v=q.top().v;q.pop();
  	if (cnt[t]>=k) continue;
  	++cnt[t];
  	if (t==td&&cnt[t]==k) return v;
  	for (int i=lin[id][t];i;i=e[i].next)
  	  if (cnt[e[i].y]<k) q.push(state(e[i].y,v-dis[t]+e[i].v+dis[e[i].y]));
  }
  return -1;
}

Abigail into(){
  scanf("%d%d",&n,&m);
  int x,y,v;
  for (int i=1;i<=m;++i){
    scanf("%d%d%d",&x,&y,&v);
    Ins(0,x,y,v);Ins(1,y,x,v);
  }
  scanf("%d%d%d",&s,&t,&k);
}

Abigail work(){
  Dijkstra(1,t);
}

Abigail outo(){
  printf("%d\n",Astar(0,s,t,k));
}

int main(){
  into();
  work();
  outo();
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值