1382:最短路(Spfa)
时间限制: 1000 ms 内存限制: 65536 KB
提交数: 3502 通过数: 1013
【题目描述】
给定 MM 条边, NN 个点的带权无向图。求 11到 NN 的最短路。
【输入】
第一行:N,M(N≤100000,M≤500000)
接下来MM行33个正整数:ai,bi,ci表示ai,bi之间有一条长度为ci的路,ci≤1000 ai,bi,ci表示ai,bi之间有一条长度为ci的路,ci≤1000。
【输出】
一个整数,表示 1 到 N 的最短距离。
【输入样例】
4 4
1 2 1
2 3 1
3 4 1
2 4 1
【输出样例】
2
【提示】
【样例解释】
注意图中可能有重边和自环,数据保证 11 到 NN 有路径相连。
【来源】信息学奥赛
分析:
(1)N≤100000,可以看出必须用邻接表,因为n的上限太大;
(2)可以看出为单源最短路径问题,可以使用dijkstra,或bellman算法,或spfa(即bellman的改良算法)
以下为dijkstra算法的实现,但是没有用优先级队列
故只得70多分
# include<iostream>
# include<cstring>
# define inf 0x3f3f3f3f
# define nil -1
using namespace std;
const int maxn=100000+5;
typedef struct arcnode{
int adjnode;
int weight;
arcnode *nextarc;
}arcnode;
typedef struct vnode{
int data;
arcnode *firstarc;
}vnode;
typedef struct graph{
vnode vertexs[maxn];
int vertexnum; //顶点数目
int edgenum; //边数目
}graph;
int dis[maxn];
int vis[maxn];
int find(graph g,int v)
{
for(int i=0;i<g.vertexnum;i++)
{
if(g.vertexs[i].data==v)
return i;
}
}
void init(graph &g)
{
cin>>g.vertexnum>>g.edgenum;
for(int i=1;i<=g.vertexnum;i++)
{
g.vertexs[i].data=i;
g.vertexs[i].firstarc=NULL;
}
int u,v,w;
for(int i=0;i<g.edgenum;i++)
{
cin>>u>>v>>w;
arcnode *s=new arcnode();
s->adjnode=v;
s->weight=w;
s->nextarc=g.vertexs[u].firstarc;
g.vertexs[u].firstarc=s;
arcnode *p=new arcnode();
p->adjnode=u;
p->weight=w;
p->nextarc=g.vertexs[v].firstarc;
g.vertexs[v].firstarc=p;
}
}
void relax(int u,int v,int w)
{
if(dis[v]>dis[u]+w)
{
dis[v]=dis[u]+w;
//pre[v]=u;
}
}
void init_signal_source(graph g,int s)
{
for(int i=1;i<=g.vertexnum;i++)
{
//pre[i]=nil;
dis[i]=inf;
}
dis[s]=0; //源点距离初始化为0
}
void dijsktra(graph g,int s)
{
init_signal_source(g,s);
for(int j=1;j<g.vertexnum;j++)// 其余g.vertexnum-1顶点
{
int min_dis=inf;
int min_index;
for(int i=1;i<=g.vertexnum;i++) //抽取最小距离索引
{
if(!vis[i]&&dis[i]<min_dis)
{
min_dis=dis[i];
min_index=i;
}
}
vis[min_index]=1;//加入集合
arcnode *w=g.vertexs[min_index].firstarc;
while(w)
{
relax(min_index,w->adjnode,w->weight);
w=w->nextarc;
}
}
}
int main()
{
graph g;
init(g);
memset(vis,0,sizeof(vis));
dijsktra(g,1);
if(dis[g.vertexnum]!=inf)
cout<<dis[g.vertexnum]<<endl;
else cout<<nil<<endl;
return 0;
}
以下是使用优先级队列的dijkstra算法,直接ac
# include<iostream>
# include<vector>
# include<queue>
# define inf 0x3f3f3f3f
# define nil -1
const int maxn=100000+5;
using namespace std;
struct edge{
int from;
int to;
int dis;
edge(int u,int v,int d):from(u),to(v),dis(d)
{}
edge()
{}
};
struct heapnode{
int d,u;
bool operator <(const heapnode& a) const
{ return a.d<d; }
heapnode(int dd,int uu):d(dd),u(uu)
{}
heapnode()
{}
};
struct Dijkstra{
int n,m;//顶点个数,边的个数
vector<edge> edges; //边集
vector<int> g[maxn]; //存放边的编号
int d[maxn]; //距离
bool vis[maxn];
void init()
{
// n=nn;
edges.clear();
for(int i=0;i<n;i++)
g[i].clear();
}
void addedge(int from,int to,int dis)
{
edges.push_back((edge){from,to,dis});
int m=edges.size();
g[from].push_back(m-1);
}
void dijkstra(int s)
{
priority_queue<heapnode> ss;
for(int i=0;i<n;i++) d[i]=inf;
d[s]=0;
ss.push((heapnode){0,s});
while(!ss.empty())
{
heapnode k=ss.top();
ss.pop();
int x=k.u;
if(vis[x]) continue;
vis[x]=true;
for(int i=0;i<g[x].size();i++)
{
edge e=edges[g[x][i]];
if(d[e.to]>d[e.from]+e.dis)
{
d[e.to]=d[e.from]+e.dis;
ss.push((heapnode){d[e.to],e.to});
}
}
}
}
};
int main()
{
Dijkstra ww;
cin>>ww.n>>ww.m;
ww.init();
for(int i=0;i<ww.m;i++)
{
int u,v,w;
cin>>u>>v>>w;
ww.addedge(u-1,v-1,w);
ww.addedge(v-1,u-1,w);
}
ww.dijkstra(0);
cout<<ww.d[ww.n-1]<<endl;
}
以下bellman算法 3个点运行超时
# include<iostream>
# include<vector>
# include<queue>
# define inf 0x3f3f3f3f
# define nil -1
const int maxn=100000+5;
using namespace std;
struct edge{
int from;
int to;
int dis;
edge(int u,int v,int d):from(u),to(v),dis(d)
{}
edge()
{}
};
struct Bellman{
int n,m;//顶点个数,边的个数
vector<edge> edges; //边集
vector<int> g[maxn]; //存放边的编号
int d[maxn]; //距离
//bool vis[maxn];
void init()
{
// n=nn;
edges.clear();
for(int i=0;i<n;i++)
g[i].clear();
}
void addedge(int from,int to,int dis)
{
edges.push_back((edge){from,to,dis});
int m=edges.size();
g[from].push_back(m-1);
}
bool bellman(int s)
{
for(int i=0;i<n;i++) d[i]=inf;
d[s]=0;
for(int i=1;i<n;i++)
{
for(int j=0;j<n;j++)
{
for(int k=0;k<g[j].size();k++)
{
edge e=edges[g[j][k]];
if(d[e.to]>d[e.from]+e.dis)
{
d[e.to]=d[e.from]+e.dis;
}
}
}
}
for(int j=0;j<n;j++)
{
for(int k=0;k<g[j].size();k++)
{
edge e=edges[g[j][k]];
if(d[e.to]>d[e.from]+e.dis)
{
return false;
}
}
}
return true;
}
};
int main()
{
Bellman ww;
cin>>ww.n>>ww.m;
ww.init();
for(int i=0;i<ww.m;i++)
{
int u,v,w;
cin>>u>>v>>w;
ww.addedge(u-1,v-1,w);
ww.addedge(v-1,u-1,w);
}
ww.bellman(0);
if(ww.d[ww.n-1]!=inf)
cout<<ww.d[ww.n-1]<<endl;
else cout<<nil<<endl;
return 0;
}
以下为spfa即bellman的改良算法
直接ac
# include<iostream>
# include<vector>
# include<queue>
# include<cstring>
# define inf 0x3f3f3f3f
# define nil -1
const int maxn=100000+5;
using namespace std;
struct edge{
int from;
int to;
int dis;
edge(int u,int v,int d):from(u),to(v),dis(d)
{}
edge()
{}
};
struct Bellman{
int n,m;//顶点个数,边的个数
vector<edge> edges; //边集
vector<int> g[maxn]; //存放边的编号
int d[maxn]; //距离
bool vis[maxn];
bool cnt[maxn];//进队次数
void init()
{
// n=nn;
edges.clear();
for(int i=0;i<n;i++)
g[i].clear();
}
void addedge(int from,int to,int dis)
{
edges.push_back((edge){from,to,dis});
int m=edges.size();
g[from].push_back(m-1);
}
bool bellman(int s)
{
queue<int> q;
memset(vis,0,sizeof(vis));
memset(cnt,0,sizeof(cnt));
for(int i=0;i<n;i++) d[i]=inf;
d[s]=0;
q.push(s);
vis[s]=1;//在队列中
while(!q.empty())
{
int k=q.front();
q.pop();
vis[k]=false;//不在队列
for(int i=0;i<g[k].size();i++)
{
edge e=edges[g[k][i]];
if(d[k]<inf&&d[e.to]>d[k]+e.dis)
{
d[e.to]=d[k]+e.dis;
if(!vis[e.to]) //如果不在队列,加入队列
{
q.push(e.to);
vis[e.to]=true;
if(++cnt[e.to]>n)
return false;
}
}
}
}
return true;
}
};
int main()
{
Bellman ww;
cin>>ww.n>>ww.m;
ww.init();
for(int i=0;i<ww.m;i++)
{
int u,v,w;
cin>>u>>v>>w;
ww.addedge(u-1,v-1,w);
ww.addedge(v-1,u-1,w);
}
ww.bellman(0);
if(ww.d[ww.n-1]!=inf)
cout<<ww.d[ww.n-1]<<endl;
else cout<<nil<<endl;
return 0;
}
总结
当n很大时 需要使用基于邻接表的并且使用优先级队列的dijkstra或基于邻接表的spfa算法