1. 程式人生 > >POJ 2449 - Remmarguts' Date - [第k短路模板題][優先佇列BFS]

POJ 2449 - Remmarguts' Date - [第k短路模板題][優先佇列BFS]

題目連結:http://poj.org/problem?id=2449

Time Limit: 4000MS Memory Limit: 65536K

Description

"Good man never makes girls wait or breaks an appointment!" said the mandarin duck father. Softly touching his little ducks' head, he told them a story. 

"Prince Remmarguts lives in his kingdom UDF – United Delta of Freedom. One day their neighboring country sent them Princess Uyuw on a diplomatic mission." 

"Erenow, the princess sent Remmarguts a letter, informing him that she would come to the hall and hold commercial talks with UDF if and only if the prince go and meet her via the K-th shortest path. (in fact, Uyuw does not want to come at all)" 

Being interested in the trade development and such a lovely girl, Prince Remmarguts really became enamored. He needs you - the prime minister's help! 

DETAILS: UDF's capital consists of N stations. The hall is numbered S, while the station numbered T denotes prince' current place. M muddy directed sideways connect some of the stations. Remmarguts' path to welcome the princess might include the same station twice or more than twice, even it is the station with number S or T. Different paths with same length will be considered disparate. 

Input

The first line contains two integer numbers N and M (1 <= N <= 1000, 0 <= M <= 100000). Stations are numbered from 1 to N. Each of the following M lines contains three integer numbers A, B and T (1 <= A, B <= N, 1 <= T <= 100). It shows that there is a directed sideway from A-th station to B-th station with time T. 

The last line consists of three integer numbers S, T and K (1 <= S, T <= N, 1 <= K <= 1000).

Output

A single line consisting of a single integer number: the length (time required) to welcome Princess Uyuw using the K-th shortest path. If K-th shortest path does not exist, you should output "-1" (without quotes) instead.

Sample Input

2 2
1 2 5
2 1 4
1 2 2

Sample Output

14

 

題意:

給出 $N$ 個頂點,$M$ 條有向邊,求從起點 $S$ 到終點 $T$ 的第 $K$ 短路的長度,路徑允許重複經過點和邊。

(如果不存在第 $k$ 短路則輸出 $-1$。其實不難知道如果從 $S$ 到 $T$ 的某條路徑上存在一個環,則必然存在第 $k$ 短路,否則就有可能沒有第 $k$ 短路)

(有一個需要注意的點是,此處當 $S=T$ 時,認為最短路徑不是零)

 

題解:

根據優先佇列優化Dijkstra演算法,我們知道,優先佇列裡面的可能存在多個元素 $(x,d_1[x]),(x,d_2[x]), \cdots$,同時代表著某個節點 $x$ 以及起點到 $x$ 的某條路徑的長度。

當從優先佇列中第一次取出 $x$ 時,起點到 $x$ 的最短路長度已經得到,那麼後續再取出節點 $x$ 顯然就是一些次短路徑。

由此我們可以想到使用優先佇列BFS

起初優先佇列中只存在元素 $(S,0)$,不斷從優先佇列中取出 $d[x]$ 最小的 $(x,d[x])$,然後沿著從 $x$ 出發的每條有向邊 $edge(x,y)$ 都進行擴充套件,將新的元素 $(y,d[y]=d[x]+w(x,y))$ 入隊(本題允許重複經過點和邊)。

不難證明,某個狀態 $(x,d_1[x])$ 第一次從優先佇列中被取出時,就得到了從初態到它的最小代價 $d[x]$。因為此時佇列中所有其他狀態的代價都大於等於 $d[x]$,是不可能再返回來更新 $d[x]$ 的。

然後,如果按照普通的優先佇列BFS做法(即優先佇列優化Dijkstra演算法),往後若再從佇列內取出另一個元素 $(x,d_2[x])$,會將其直接忽略,因為必然不是最優解。

那麼,不難想到在本題的優先佇列BFS做法中,第 $i$ 次從優先佇列中取出的元素 $(x,d_i[x])$,其 $d_i[x]$ 便是從起點到 $x$ 的第 $i$ 短路長度。

所以,最終當終點 $T$ 從佇列中第 $K$ 次出隊時,便求得了答案。

這樣演算法最壞的時間複雜度為 $O(K(N+M)log(N+M))$。

 

MLE程式碼:

#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
using namespace std;
typedef pair<int,int> pii;
const int maxn=1005;
int n,m,S,T,K;

struct Edge{
    int v,w;
    Edge(){}
    Edge(int _v,int _w) {
        v=_v, w=_w;
    }
};
vector<Edge> E[maxn];

priority_queue< pii, vector<pii>, greater<pii> > Q;
int cnt[maxn];
int bfs()
{
    memset(cnt,0,sizeof(cnt));
    Q.push(make_pair(0,S));
    while(!Q.empty())
    {
        int x=Q.top().second, d=Q.top().first; Q.pop(); cnt[x]++;
        if(cnt[T]==K) return d;
        for(int k=0;k<E[x].size();k++)
        {
            int y=E[x][k].v, w=E[x][k].w;
            if(cnt[y]<K) Q.push(make_pair(d+w,y));
        }
    }
    return -1;
}

int main()
{
    cin>>n>>m;
    for(int i=1,u,v,w;i<=m;i++)
    {
        scanf("%d%d%d",&u,&v,&w);
        E[u].push_back(Edge(v,w));
    }
    cin>>S>>T>>K;
    if(S==T) K++;
    cout<<bfs()<<endl;
}

交了一發,得到MLE $\times 1$,應該是優先佇列記憶體的元素太多了……

 

A*優化:

我們知道,A*用一個估價函式 $f(x)$ 來估計當前狀態到目標狀態所需代價。

在本題中,實際代價 $g(x)$ 即代表:第 $k$ 短路中從 $x$ 到 $T$ 的實際距離。

顯然,相應的我們可以設 $f(x)$ 代表:最短路中從 $x$ 到 $T$ 的實際距離。

這樣一來,必然滿足條件 $f(x) \le g(x)$,正確性得以保證。

優先佇列內原本儲存的是 $(x,d[x])$,現在變為 $(x,d[x]+f(x))$。

A*演算法的時間複雜度上界一樣是 $O(K(N+M)log(N+M))$,但由於估價函式 $f(x)$ 的作用,很多節點訪問次數遠小於 $k$,是有較明顯的加速的(相應的優先佇列記憶體儲的元素會大幅度減少,避免MLE)。

 

AC程式碼:

#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
using namespace std;
typedef pair<int,int> pii;
const int maxn=1005;
const int INF=0x3f3f3f3f;
int n,m,S,T,K;

struct Edge{
    int v,w;
    Edge(){}
    Edge(int _v,int _w) {
        v=_v, w=_w;
    }
};
vector<Edge> E[maxn],fE[maxn];
priority_queue< pii, vector<pii>, greater<pii> > Q;

int f[maxn];
bool vis[maxn];
void dijkstra()
{
    memset(f,0x3f,sizeof(f));
    memset(vis,0,sizeof(vis));
    f[T]=0;
    Q.push(make_pair(0,T));
    while(Q.size())
    {
        int u=Q.top().second; Q.pop();
        if(vis[u]) continue;
        vis[u]=1;
        for(int i=0;i<fE[u].size();i++)
        {
            int v=fE[u][i].v, w=fE[u][i].w;
            if(vis[v]) continue;
            if(f[v]>f[u]+w)
            {
                f[v]=f[u]+w;
                Q.push(make_pair(f[v],v));
            }
        }
    }
}

int cnt[maxn];
int bfs()
{
    memset(cnt,0,sizeof(cnt));
    Q.push(make_pair(0,S));
    while(!Q.empty())
    {
        int x=Q.top().second, d=Q.top().first; Q.pop(); cnt[x]++;
        if(cnt[T]==K) return d;
        for(int k=0;k<E[x].size();k++)
        {
            int y=E[x][k].v, w=E[x][k].w;
            if(cnt[y]<K) Q.push(make_pair(d+w,y));
        }
    }
    return -1;
}

int A_star()
{
    memset(cnt,0,sizeof(cnt));
    Q.push(make_pair(0+f[S],S));
    while(Q.size())
    {
        int u=Q.top().second;
        int d=Q.top().first-f[u];
        Q.pop(); cnt[u]++;
        if(cnt[T]==K) return d;
        for(int i=0,v,w;i<E[u].size();i++)
        {
            v=E[u][i].v, w=E[u][i].w;
            if(cnt[v]<K) Q.push(make_pair(d+w+f[v],v));
        }
    }
    return -1;
}

int main()
{
    cin>>n>>m;
    for(int i=1,u,v,w;i<=m;i++)
    {
        scanf("%d%d%d",&u,&v,&w);
        E[u].push_back(Edge(v,w));
        fE[v].push_back(Edge(u,w));
    }
    cin>>S>>T>>K;
    if(S==T) K++;
    dijkstra();
    cout<<A_star()<<endl;
}