引言:
最短路大家都不陌生吧,他可以是两点之间的最短路(Floyd),也可以是单源最短路,即一个点到其他点的最短距离(Dijkstra和SPFA)。Floyd对于n个顶点(n>100)的图,就会T,所以我们经常用后面两种算法。
相关题目:
HDU-1874:畅通工程续
这道题吧,随便一种方法都能过…很神奇。
1.Floyd:
for(int k=0;k<n;k++){
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(mp[i][j]>mp[i][k]+mp[k][j]) mp[i][j]=mp[i][k]+mp[k][j];
}
}
}
就是找一个中间点k,如果由i到k,再经过j的距离比直接从i到j的短,就更新mp[i][j]的值,很暴力的O(n^3)。
2.Dijkstra:
他的思想就是疏松边,dis数组存储起点到其他点的最短距离,初始化为inf。题目的坑点是用这个方法,要考虑重边的情况,这时候取最小的那一条。对于本算法,每次取最小dis值得那个点进行访问,我们每访问过一个点就要标记为一,然后对他延申出来的边进行疏松。
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define maxn 20005
const int inf=0x3f3f3f3f;
typedef long long ll;
int main(){
int n,m,mp[205][205],vis[205],dis[205];
while(~scanf("%d%d",&n,&m)){
int u,v,w,x,y,kmin,k;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(i!=j) mp[i][j]=inf;
else mp[i][j]=0;
}
}
memset(vis,0,sizeof vis);
for(int i=0;i<n;i++) dis[i]=inf;
while(m--){
scanf("%d%d%d",&u,&v,&w);
mp[u][v]=min(mp[u][v],w);
mp[v][u]=min(mp[v][u],w);
}
scanf("%d%d",&x,&y);
dis[x]=0;
for(int i=0;i<n;i++){
kmin=inf;
for(int j=0;j<n;j++){
if(vis[j]==0&&dis[j]<kmin){
kmin=dis[j];
k=j;
}
}
vis[k]=1;
for(int j=0;j<n;j++){
if(!vis[j]&&dis[j]>dis[k]+mp[k][j]){
//判断vis[j]等不等于0是达到一种剪枝得效果
dis[j]=dis[k]+mp[k][j];
}
}
}
if(dis[y]!=inf) printf("%d\n",dis[y]);
else printf("-1\n");
}
return 0;
}
3.链式前向星+SPFA:
这才是重点,我研究了很久,才理解到了他的大概意思。链式前向星就是一个结构体加一个数组:
int cnt;
struct edge{
int w;
int to;
int next;
}eg[maxn*3];
int head[maxn];
然后这个结构体,存储的是边,w是边的权值,to表示以i为起点的边的终点,next表示同起点的下一条边,head表示以i为起点的第一条边存储的位置,其实就是我们输入的边的最后一条以i为起点的编号。为什么呢?,因为我们每次输入u,v,w,都要调用addedge函数。即:
for(int i=0;i<m;i++){
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
addedge(v,u,w);
}
链式前向星核心函数:
void addedge(int u,int v,int w){
eg[cnt].w=w;
eg[cnt].to=v;
eg[cnt].next=head[u];
head[u]=cnt;
cnt++;
}
推荐一个详细的大佬的链式向前星:看大佬的链式向前星请点击这里
接下来我们回到SPFA算法:
这个算法就是每次将一个点放入队列中,然后将与他邻接的点对应的边进行疏松,若该点不在队列中,就将他入队,并标记vis[i]=1。访问完了之后,注意要将首元素出队,并解除标记,即:vis[i]=0!!!这也是跟Dijkstra最大的区别。因为你下次疏松仍然可以用到这个点。
完整代码:
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define maxn 1005
const int inf=0x3f3f3f3f;
typedef long long ll;
int cnt;
struct edge{
int w;
int to;
int next;
}eg[maxn*3];
int head[maxn];
void addedge(int u,int v,int w){
eg[cnt].w=w;
eg[cnt].to=v;
eg[cnt].next=head[u];
head[u]=cnt;
cnt++;
}
int main(){
int n,m,u,v,w,sx,sy,dis[maxn],vis[maxn],k,t;
while(scanf("%d%d",&n,&m)!=EOF){
cnt=0;
for(int i=0;i<n;i++){
vis[i]=0;
dis[i]=inf;
head[i]=-1;
}
for(int i=0;i<m;i++){
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
addedge(v,u,w);
}
scanf("%d%d",&sx,&sy);
dis[sx]=0;
queue<int> q;
q.push(sx);
vis[sx]=1;
while(!q.empty()){
k=q.front();
q.pop();
for(int i=head[k];i!=-1;i=eg[i].next){
int id=eg[i].to;
if(dis[id]>dis[k]+eg[i].w){
dis[id]=dis[k]+eg[i].w;
if(!vis[id]){
vis[id]=1;
q.push(id);
}
}
}
vis[k]=0;
}
if(dis[sy]==inf) printf("-1\n");
else printf("%d\n",dis[sy]);
}
return 0;
}