JZOJ 1163. 第K短路(A*)

JZOJ 1163. 第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

题解

  • 这是一道A*算法的模板题。
  • 它并不是一道用A*来优化DFS,而是这题的求解过程和A*算法思路类似。
  • 设估价函数 F ( x ) = G ( x ) + H ( x ) F(x)=G(x)+H(x) F(x)=G(x)+H(x)
  • G ( x ) G(x) G(x)是当前已经走了的距离, H ( x ) H(x) H(x) x x x n n n的实际最短路,
  • 先建反向边跑一遍最短路求出所有的 H ( x ) H(x) H(x),然后从 1 1 1开始按A*的思路求解,
  • 每次在队列里取出 F ( x ) F(x) F(x)最小的点,把它能连向的点加入队列,使其 F ( x ) = G ( x ) + H ( x ) F(x)=G(x)+H(x) F(x)=G(x)+H(x)
  • 当第 K K K次取出终点 N N N时,当前的距离就是第 K K K短路。
  • 但要注意,本题中相同路径长度算作相同的路径,所以还要记录上一次取出 N N N时的距离 l a s t last last,只有当前距离 G ( x ) > l a s t G(x)>last G(x)>last时才 c n t + 1 cnt+1 cnt+1
  • 还有一个优化,对于每个点都记录一个 c n t [ x ] cnt[x] cnt[x],如果 c n t [ x ] > K cnt[x]>K cnt[x]>K则不需要再取出点 x x x,因为到 x x x已经有了 K K K种不同的路径长度,总有一种会是到终点的第 K K K短路的一部分。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 10010
#define M 100010
struct
{
	int x,y,l;
}a[M];
int last[N],nxt[M],to[M],ln[M],len=0;
int dis[N],q[N],vi[N],s=1,cnt[N],ls[N];
struct node
{
	int x,s,t;
}f[M*20];
void add(int x,int y,int l)
{
	to[++len]=y;
	nxt[len]=last[x];
	ln[len]=l;
	last[x]=len;
}
void down(int k)
{
	while((k*2<=s&&f[k*2].s<f[k].s)||(k*2<s&&f[k*2+1].s<f[k].s))	
	{
		int l=k*2;
		if(k*2<s&&f[k*2+1].s<f[k*2].s) l++;
		swap(f[k],f[l]);
		k=l;
	}
}
void up(int k)
{
	while(k>1&&f[k/2].s>f[k].s) 
	{
		swap(f[k],f[k/2]);
		k/=2;
	}
}
int main()
{
	int n,m,k,i;
	scanf("%d%d%d",&n,&m,&k);
	for(i=1;i<=m;i++)
	{
		scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].l);
		add(a[i].y,a[i].x,a[i].l);
		char ch;
		while(ch=getchar(),ch!=10);
	}
	memset(dis,127,sizeof(dis));
	dis[n]=0,q[1]=n,vi[n]=1;
	int l=0,r=1;
	while(l!=r)
	{
		l=l%(n+5)+1;
		int x=q[l];
		for(i=last[x];i;i=nxt[i]) 
		{
			int y=to[i];
			if(dis[x]+ln[i]<dis[y])
			{
				dis[y]=dis[x]+ln[i];
				if(!vi[y])
				{
					vi[y]=1;
					r=r%(n+5)+1;
					q[r]=y;
				}
			}
		}
		vi[x]=0;
	}
	memset(last,0,sizeof(last));
	len=0;
	for(i=1;i<=m;i++) add(a[i].x,a[i].y,a[i].l);
	f[1].s=dis[1],f[1].t=0,f[1].x=1;
	while(1)
	{
		int x=f[1].x,t=f[1].t;
		f[1]=f[s];
		s--;
		down(1);
		if(t>ls[x]) cnt[x]++,ls[x]=t;
		if(cnt[n]==k)
		{
			printf("%d",t);
			break;
		}
		if(cnt[x]>k) continue;
		for(i=last[x];i;i=nxt[i])
		{
			f[++s].x=to[i];
			f[s].t=t+ln[i];
			f[s].s=f[s].t+dis[to[i]];
			up(s);
		}
	}
	return 0;
}
posted @ 2020-01-12 21:52  AnAn_119  阅读(51)  评论(0编辑  收藏  举报