1. 程式人生 > >poj2449 Remmarguts' Date(第k短路問題)(A*+spfa/dijkstra)

poj2449 Remmarguts' Date(第k短路問題)(A*+spfa/dijkstra)

思路來源

https://blog.csdn.net/berrykanry/article/details/78345894(通俗易懂解釋好評)

https://www.cnblogs.com/yyf0309/p/8438849.html(可惜看不懂可持久化可並堆)

題意

給定一個圖,求第k短路,

相同長度不同路徑的路被認為是不同的路,

若不存在輸出-1。

題解

我才不會說我八數碼題tleMLE了兩天後補了一下A*演算法又被安利來看這個破題

以終點為S進行dijkstra/spfa,

(具體實現可以建反向圖)

然後得到了真實的評估函式h(n)的表, 

這樣我到了一個點,就知道已走路g(n)+前方最短路h(n)這條路的cost,

把它記為截止到該點的理想最短路。

 

顯然,我們從起點dis[n]即最短路開始延展,

相當於bfs,由起點轉向其一步可達點,

即由最短路,轉向了後續狀態可能換了一條邊的最短路。

後者是實際意義上的次短,第三短等等等…

 

而沒換邊的路,被加進優先佇列裡之後,

由於其距離小,還應處在隊頂的位置,只是其截止點往後推了一個,

若不存在環,最終所有點的截止點都會被推到終點,

而我們只需要這其中的前k個。

 

而由於優先佇列按距離排序,

當其到達終點的時候,對終點計貢獻,

說明這些路是截止到終點的,長度嚴格遞增的路。

當計到第k時,即為起點可達終點的k短路。

 

這個題,引發了我深深的感慨。

 

在人生之路上,當你還在為自己的前途備一個list,

記錄第一志願,第二志願,……,第k志願並在線為其比較的時候,

早就有人已經洞若觀火,知道自己第一志願應該報哪,

並且,為了選擇第k志願,明明知道最短路在哪,

不惜比照著自己已經打好的dijkstra表,

開始往那些非最短路的地方瞎走???

瞎走一步,啊,這次走到了次短路,加到佇列裡,

瞎走兩步…瞎走k步,啊終於找到了我要的k短路

高考空了50分 終於考上了理想第k大學 既視感

程式碼

#include <iostream>
#include <algorithm> 
#include <cstring>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <vector>
#include <stack>
#include <queue>
#include <functional>
const int INF=0x3f3f3f3f;
const int maxn=1e5+10; 
const int mod=1e9+7;
const int MOD=998244353;
const double eps=1e-7;
typedef long long ll;
#define vi vector<int> 
#define si set<int>
#define pii pair<int,int> 
#define pi acos(-1.0)
#define pb push_back
#define mp make_pair
#define lowbit(x) (x&(-x))
#define sci(x) scanf("%d",&(x))
#define scll(x) scanf("%lld",&(x))
#define sclf(x) scanf("%lf",&(x))
#define pri(x) printf("%d",(x))
#define rep(i,j,k) for(int i=j;i<=k;++i)
#define per(i,j,k) for(int i=j;i>=k;--i)
#define mem(a,b) memset(a,b,sizeof(a)) 
using namespace std;
int n,m,s,t,k;
int head[1005],cnt,rehead[1005],qq[5005];
bool vis[1005];
int dis[1005];
struct edge{int to,nex,w;}e[100005],re[100005];
struct node
{
   int g;//當前已走 
   int f;//總 
   int id;//節點號 
   node(int a,int b,int c):g(a),f(b),id(c){
   }
};
bool operator>(node a,node b)
{
	if(a.f!=b.f)return a.f>b.f;
	return a.g>b.g;
}
priority_queue<pii,vector<pii>,greater<pii> >q;
priority_queue<node,vector<node>,greater<node> >p; 
void init()
{ 
	mem(head,-1);
	cnt=0;
	mem(rehead,-1);
	mem(vis,0);
}
void add(int u,int v,int w)
{
	e[cnt].to=v;//u->v
	e[cnt].w=w;
	e[cnt].nex=head[u];
	head[u]=cnt;
	re[cnt].to=u;//v->u
	re[cnt].w=w;
	re[cnt].nex=rehead[v];
	rehead[v]=cnt++;
}

void spfa(int src)
{
    for(int i = 1; i <= n; i++) dis[i] = INF;
    mem(vis,0);
    int h = 0, t = 1;
    qq[0] = src;
    dis[src] = 0;
    while(h < t)
    {
        int u = qq[h++];
        vis[u] = 0;
        for(int i = rehead[u] ; i != -1; i = re[i].nex)
        {
            int v = re[i].to;
            int w = re[i].w;
            if(dis[v] > dis[u] + w)
            {
                dis[v] = dis[u] + w;
                if(!vis[v])
                {
                    qq[t++] = v;
                    vis[v] = 1;
                }
            }
        }
    }
}
int astar(int s,int t)
{
	int num=0;
	if(s==t)k++;//注意這裡,我們把s->s視作為0的最短路
	if(dis[s]==INF)return -1;
	while(!p.empty())p.pop();
	p.push(node(0,dis[s],s));//以t為視角的距離 
	while(!p.empty())
	{
		node tmp=p.top();
		p.pop();
		int g=tmp.g,f=tmp.f,u=tmp.id;
		if(u==t)
		{
		 num++; 
		 if(num==k)return g;
	    }
		for(int j=head[u];~j;j=e[j].nex)
		{
			int v=e[j].to,w=e[j].w;
			p.push(node(g+w,g+w+dis[v],v));
		}
	}
	return -1;
}
int main()
{    
    while(~scanf("%d%d",&n,&m))
    {
    init();
    rep(i,0,m-1)
    {
    	int u,v,w;
    	scanf("%d%d%d",&u,&v,&w);
    	add(u,v,w);
    }
    scanf("%d%d%d",&s,&t,&k);
    spfa(t);//對終點dijkstra
	printf("%d\n",astar(s,t));
    }
	return 0;
}