[テンプレート] SPFA(完全には説明しません)

最も短絡評価方法の一つ(使いやすいDIJKSTRAより個人的な感触)     

図に指示しました。

おそらく思考:チームが空にアップになるまでこの操作を繰り返し、パスが更新された場合、その更新Unicom社のポイントへの最短経路ながら、チームにこの点を入れ、すべてのポイントを列挙し、ルートから始まります。  

コード:

構造体のエッジは{
     int型次に、へ、V; 
} E []; // 図、次のエッジの同ヘッドの代わりに次のノードを保存

ボイド SPFA(int型S){
     int型のP、X、Y、Lは、R
     ( = X 1 ; Xは、<= N-; ++ X)
        DIS [X] = ; INF 
    Q [ 0 ] = S、DIS [S] = 0、V [S] = 1 ; // 初期化
    するための(L = R&LT = 0、L =(R&LT +!1。 )%N;){ 
        P = Q [L]、++ = Lの%N; //は最初のチームを取り出す
        ため(X =はじめ[P]; X、X = E [X] .next)//トラバース各側のヘッドとキューの先頭
            IF(DIS [P] + E [X] .V <DIS [(Y = E [X] .TO)]){ // 更新場合 
                DIS [Y]はDIS [P] + E [X] .V =; // 更新
                IF!(V [Y]){ 
                    V [Y] =を1 ; 
                    Q [ ++ = N R&LTの%] = Y; // エンキュー
                } 
            } 
        V [P] = 0 ; 
    } 
}

SLFの最適化を使用することができますので、しかし、このアプローチは、容易、(魔法の牛)を貼り付けることができます。

SLFの最適化:チームは値が最初のチームよりも小さい場合、最初のチームに、チームの最初の値の比較の価値を考えるつもりだったたびに。

なぜ?

毎回のチームが最初のノードが最小になるように、最小値が更新されたときに最初のチームからトラバースが存在しますので、これは時間の大部分を保存することができます。

(やや抽象..例を与えるために)

(私は描きたくありません)

 

这里需要注意:只有dis[]存储路径值,q[]存储的是当前最小路径值所在的位置,包括edge里的next也是同一个起点的上一对点的序号。(这里的头就是First[]的下标)

这里各种各样的序号很多。。特别容易弄混。会把各种序号分段输出的程序放在结尾,看不懂的话试几组样例看看输出会很有帮助。

到这里只是第一次更新。

下面是第二次更新:

 

接下来就可以以此类推了。。如果看不懂的话下面是分段输出的代码,结合上面的图看,体会一下中心思想。

代码

#include<iostream>
using namespace std;
struct edge {
    int next, to, v;
    edge(){}
    edge(int x,int y,int z)
    {
        next=x;
        to=y;
        v=z;
    }
}e[10001];

int first[10001];
int tot;
int dis[100001];
int q[100001];
int v[100001];
void add_edge(int x, int y,int z) {
    e[++tot] = edge(first[x], y,z);
    first[x] = tot;
}
int n;
int N=31;

int inc(int x) {
    x = x + 1;
    x = x % N;
    return x;
}

int dec(int x) {
    x = x - 1 + N;
    x = x % N;
    return x;
}

void spfa(int S) {
    int p, x, y, l, r;
    for (x = 1; x <= n; ++x)
        dis[x] = 0x7ffff;
    q[0] = S, dis[S] = 0, v[S] = 1;
    for (l = r = 0; l != (r + 1) % N; ) {
        p = q[l];
        cout<<"从第"<<p<<"号边开始遍历"<<endl; 
        l=inc(l);
        for (x = first[p]; x; x = e[x].next)
        {
            cout<<"这时是第"<<x<<"号边"<<endl; 
            if (dis[p] + e[x].v < dis[(y = e[x].to)]) {
                cout<<"更新"<<" "<<""<<dis[y]; 
                dis[y] = dis[p] + e[x].v; 
                cout<<"更新为"<<dis[y]<<endl; 
                if (!v[y]) {
                    v[y] = 1;
                    if (dis[y] < dis[q[l]]) 
                    {q[(l=dec(l))] = y;
                    cout<<"从队首插入"<<y<<endl; 
                    cout<<"这时l为"<<l<<"r不变"<<endl; 
                    } 
                    else 
                    {q[(r=inc(r))] = y;
                    cout<<"从队尾插入"<<y<<endl; 
                    cout<<"这时l不变r变为"<<r<<endl; }
                    cout<<"更新后的队列为"<<endl; 
                    for(int i=0;i<=n;i++)
                    {
                    cout<<q[i]<<" "; 
                    } cout<<endl;
                    cout<<"入队情况为"<<endl;
                    for(int i=1;i<=n;i++)
                    cout<<""<<i<<"号元素"<<v[i]<<" ";
                    cout<<endl;
                }
            }
        }
        v[p] = 0;
        cout<<"此时最短路径被更新为"<<endl; 
        for(int i=0;i<=n;i++)
        {
            cout<<dis[i]<<" ";
        }
        cout<<endl;
    }
}


int main()
{
    int m,S;
    cin>>n>>m>>S;
    int x,y,z;
    for(int i=1;i<=m;i++)
    {
        cin>>x>>y>>z;
        add_edge(x,y,z);
    }
    
    spfa(S);
    cout<<"最终最短路径"<<endl;
    for(int i=1;i<=n;i++)
    {
        cout<<dis[i]<<" ";
     } 
    
    cout<<"一共有"<<tot<<"个节点,分别是"<<endl;
    for(int i=1;i<=tot;i++)
    {
        cout<<"与它同起点的上一对点为"<<e[i].next<<""<<" "<<"它指向"<<e[i].to<<"这个点"<<endl;
    }
    cout<<"下面输出First数组"<<endl;
        for(int i=1;i<=tot;i++)
    {
        cout<<first[i]<<" ";
    }

}

这是举例用的样例:

4 6 1
1 2 2
2 3 2
2 4 1
1 3 5
3 4 3
1 4 4
到这里就结束啦!希望可以看懂!
蒟蒻的第二篇博客(再放个烟花吧!)

おすすめ

転載: www.cnblogs.com/Daz-Os0619/p/11388157.html