迪傑斯特拉與spfa

Drogal_dracarys發表於2020-10-14

比較全的一個部落格
https://www.cnblogs.com/wozuishuaiwozuiniu6/p/13178762.html

spfa
SPFA 演算法是 Bellman-Ford演算法 的佇列優化演算法的別稱,通常用於求含負權邊的單源最短路徑,以及判負權環。SPFA 最壞情況下複雜度和樸素 Bellman-Ford 相同,為 O(VE)。

洛谷P1462 【通往奧格瑞瑪的道路】

再被題面軍搞懵一陣子後發現這貌似是一個求最大值最小的問題(還真是)。

哦,找上界嘛,使這個上界在能跑到n點的情況下儘可能的小,當然上界就是f [i] 啦。可以加一個mon陣列,排遍序,二分比較方便。

二分答案套模板,把judge換成spfa版,具體看程式碼,框架是二分答案的架子,主體是spfa。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>
#include<cmath>
using namespace std;
queue <int> que;
struct Edge{
    int next;
    int to;
    int key;
}edge[100005];
int cnt=0,head[50001],n,m,b,f[50001],mon[50001],r,l,mid,ans;
long long dis[50001];
bool vis[50001];
int read()
{
    int r=0,k=1;
    char c=getchar();
    for(;c<'0'||c>'9';c=getchar())if(c=='-')k=-1;
    for(;c>='0'&&c<='9';c=getchar())r=r*10+c-'0';
    return r*k;
}
void add(int x,int y,int z)
{
    ++cnt;
    edge[cnt].next=head[x];
    head[x]=cnt;
    edge[cnt].to=y;
    edge[cnt].key=z;
}
bool spfa(int sj)
{
    memset(dis,0x7f,sizeof(dis));
    memset(vis,0,sizeof(vis));
    dis[1]=0;
    vis[1]=1;
    que.push(1);
    while(!que.empty())
    {
        int x=que.front();
        que.pop();
        vis[x]=0;
        for(int i=head[x];i;i=edge[i].next)
        {
            int will=edge[i].to;
            if(f[will]>sj) continue;
            if(dis[x]+edge[i].key<dis[will])
            {
                dis[will]=dis[x]+edge[i].key;
                if(!vis[will])
                {
                    vis[will]=1;
                    que.push(will);
                }
            }
        }
    }
    if(dis[n]>=b||dis[n]==dis[0]) return 0;
    else return 1;
}
int main()
{
    n=read();
    m=read();
    b=read();
    for(int i=1;i<=n;i++) mon[i]=f[i]=read();
    sort(mon+1,mon+1+n);
    for(int i=1;i<=m;i++)
    {
        int x,y,z;
        x=read();
        y=read();
        z=read();
        add(x,y,z);
        add(y,x,z);
    }
    l=1,r=n;
    if(!spfa(1000000001))
    {
        cout<<"AFK"<<endl;
        return 0;
    }
    while(l<=r)
    {
        mid=(l+r)>>1;//二分金錢 
        if(spfa(mon[mid]))//看看在此情況下,血量是否允許 
        {
            r=mid-1;
            ans=mon[mid];
        }
        else l=mid+1; 
    }
    cout<<ans<<endl;
    return 0;
}

迪傑斯特拉
時間複雜是 O(n2+m)O(n2+m), nn 表示點數,mm 表示邊數

int g[N][N];  // 儲存每條邊
int dist[N];  // 儲存1號點到每個點的最短距離
bool st[N];   // 儲存每個點的最短路是否已經確定

// 求1號點到n號點的最短路,如果不存在則返回-1
int dijkstra()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;

    for (int i = 0; i < n - 1; i ++ )
    {
        int t = -1;     // 在還未確定最短路的點中,尋找距離最小的點
        for (int j = 1; j <= n; j ++ )
            if (!st[j] && (t == -1 || dist[t] > dist[j]))
                t = j;

        // 用t更新其他點的距離
        for (int j = 1; j <= n; j ++ )
            dist[j] = min(dist[j], dist[t] + g[t][j]);

        st[t] = true;
    }

    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}

相關文章