• 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;
    }
    
    哈哈哈哈哈哈哈哈哈哈
  • 相关阅读:
    有效的括号
    数组
    复杂度分析
    技术派-epoll和IOCP之比较
    2020 University Rankings US News(美国)
    2020 University Rankings US News(亚洲)
    2020 University Rankings US News(中国)
    技术派-如果编译提示winnt.h(222):error C2146错误
    技术派-github常见的一些用法和缩写
    技术派-9个常用的代码托管平台
  • 原文地址:https://www.cnblogs.com/LZA119/p/13910049.html
Copyright © 2020-2023  润新知