最短路(spfa)

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算法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值