題目描述:
如題,給出一個有向圖,請輸出從某一點出發到所有點的最短路徑長度。
輸入格式:
第一行包含三個整數 n,m,s分別表示點的個數、有向邊的個數、出發點的編號。
接下來 m 行每行包含三個整數 u,v,w表示一條 u →v 的,長度爲 w 的邊。
輸出格式:
輸出一行 n 個整數,第 i 個表示 s 到第 i 個點的最短路徑,若不能到達則輸出 2^31-1。
輸入輸出樣例:
輸入:
4 6 1
1 2 2
2 3 2
2 4 1
1 3 5
3 4 3
1 4 4
輸出:
0 2 4 3
分析&說明:
學習了基礎的鬆弛操作後,我們來學習最樸素的Floyd算法:
#include<iostream>
#include<cstdio>
using namespace std;
long long a[7005][7005],s,n,m,u,v,d;
const int inf=0x7fffffff;
int main()
{
cin>>n>>m>>s;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
a[i][j]=inf;
//把鄰接矩陣數組重置爲inf
}
}
for(int i=1;i<=m;i++)
{
cin>>u>>v>>d;
a[u][v]=min(a[u][v],d);
//鄰接矩陣存儲
}
for(int k=1;k<=n;k++)
{
for(int i=1;i<=n;i++)
{
if(i==k||a[i][k]==inf)
{
continue;
}
for(int j=1;j<=n;j++)
{
a[i][j]=min(a[i][j],a[i][k]+a[k][j]);
}//鬆弛操作
}
}
a[s][s]=0; //起點標記爲0
for(int i=1;i<=n;i++)
{
printf("%d ",a[s][i]);
//輸出
}
return 0;
}
然而我鍾愛的Floyed在洛谷上只有40分。
所以只能用Spfa了!
Spfa代碼:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,m,s;
struct node
{
int to,next,w;
}a[510000];//鄰接鏈表存圖,來節省空間
int head[500100],vis[510000],dis[510000],st[510000];
int tot=0;
void init()
{
memset(dis,0x3f,sizeof(dis));
for(int i=1;i<=m;i++)
{
head[i]=-1;
}
}
void add(int u,int v,int w)
{
a[++tot].to=v;
a[tot].w=w;
a[tot].next=head[u];
head[u]=tot;
}
void spfa()
{
int u,l=0;
int r=1;
st[1]=s;
vis[s]=1;
dis[s]=0;//上面幾步爲SPFA的初始化
while(l<r)
{
u=st[++l];//隊列的第一個元素出列,然後找該元素相連的其它點
vis[u]=0;//出列操作
for(int i=head[u];i!=-1;i=a[i].next)
{
if(dis[a[i].to]>dis[u]+a[i].w)//如果新的邊比原先的短,那就更新權值
{
dis[a[i].to]=dis[u]+a[i].w;
if(vis[a[i].to]==0)//如果被更新的點原先不在隊列裏,就把它加進來,這樣做的好處是避免了每一個點的重複詢問,降低了時間複雜度
{
vis[a[i].to]=1;
st[++r]=a[i].to;//被更新的點加入隊列
}
}
}
}
}
int main()
{
int u,v,w;
cin>>n>>m>>s;
init();
for(int i=1;i<=m;i++)
{
cin>>u>>v>>w;
add(u,v,w);
}
spfa();//SPFA主體
for(int i=1;i<=n;i++)
{
if(i==s) cout<<0<<" ";
else
{
if(dis[i]==0x3f3f3f3f) cout<<2147483647<<" ";//如果沒有被連,那就輸出2147483647
else cout<<dis[i]<<" ";
}
}
return 0;
}
/*
4 6 1
1 2 2
2 3 2
2 4 1
1 3 5
3 4 3
1 4 4
*/