最短路

一.Bellman-Ford

主要适用场景:
1.这主要是一个用来判断是否存在负环的
2.当边权可为负数时Dijkstra算法不成立!这个可以很容易去判断!!!
所以此时只能去弄 Bellman-Ford !!
Bellman-Ford
用 dist[i] 去记录所有节点的最短路径!!
理解:首先确定一个目标顶点然后把其距离赋值为0,然后把其他赋值为无穷大然后不断用边去更新所有的节点看看最短路是否能被更新,一开始能被更新的一定是起点所连出去的然后不断拓展出去,但是边更新时是一条条边枚举过去更新的,故可能最后才能被更新到,且每次更新时可能只更新到一个点,(边的编号在最后,要跑到最后才被更新,而前面又不能更新,所以最差去看下只能更新一个点,此时复杂度O(NM)!!)而且因为这个原因只要有点能被更新,操作就得继续,可能被更新的点连出去的边还能更新其他的点!!没被更新的点不可能更新其他点(确信!!
这就是算法的核心吧,不断去更新点!!这样代码便容易写出!!

struct edge{
	int x,y,z;
}edge[M+1];
int n,m,dist[N+1],pre[N+1];
int bfs(int s,int t)
{
  memset(dist,127,sizeof(dist));
  memset(pre,0,sizeof(pre));
  dist[s]=0;
  while(1)
    {
	bool ok=false;
	for(int i=1;i<=m;i++)//最差迭代 n-1 次每次只能枚举到一条有效的边 
	{
   int x=edge[i].x,y=edge[i].y,v=edge[i].z;  //每一次迭代去看所有边是否能被更新!!!
	  if(dist[x]<1<<30)
	     {
	      if(dist[x]+v<dist[y])
		{
		dist[y]=dist[x]+v;
		pre[y]=x;//记录一个状态数组从哪里来的
		ok=true;  // 能被更新才有下一次更新!!
		}
	      }
	}
	if(!ok)
	break;
    }
	return dist(t);
}

当然还有一个队列优化(spfa 但是不推荐用因为很容易被卡不如跑一个Dijkstra
这样更新边也快好像不要跑一个 o(nm) 了跑边只需跑一些
那就便可完全摒弃bellman——ford直接跑一个spfa
不过当边权出现负值时

void spfa(int s,int t)
{
	memset(dist,127,sizeof(dist));
	memset(st,0,sizeof(st));
	dist[s]=0;
	int front=1,rear=1;
	q[rear]=s;
	st[s]=true;  //现在在队列里面
	while(front<=rear)
	{
		int x=q[front++];  //出队现在不在队列里面
		st[x]=false;       // 队列x可重新入队!!
		for(auto i:edge[x])
		{
			if(dist[i.first]>dist[x]+i.second)
			{
			dist[i.first]=dist[x]+i.second;
			if(!st[i.first])
			{
				q[++rear]=i.first;
				st[i.first]=true;
			}
			}
		}
	}
	if(dist[t]<1<<30) cout<<dist[t]<<'\n';
	else cout<<-1<<'\n';
}

找一个点到多个点最短路的和 ,可以去从终点开始找每个点的最短路,然后去看哪个点加起来最小!!

这道题就是一个典型题目逆向的一个思维!!

#include <bits/stdc++.h>
using namespace std;
int n,m,k,d[20001];
vector<pair<int,int>> edge[20001];
vector<int> q[20001];
int main()
{
	cin>>n>>m>>k;
	int i;
	for(i=1;i<=m;i++)
	{
		int x,y,z;
		cin>>x>>y>>z;
		edge[x].push_back({y,z});
		edge[y].push_back({x,z});	
	}
	while(k--)
	{
		int fx,fy;
		cin>>fx>>fy;
		for(i=0;i<=n;i++)
		q[i].clear();
		memset(d,255,sizeof(d));
		d[fx]=0;
		q[0].push_back(fx);
		for(i=0;!q[i].empty();i++)
		{   //这样去写队列的原因 :vector 从0开始存东西的!!
			int front=0,rear=q[i].size()-1;   //先把所有起始点为0的加入进入队列中
			while(front<=rear)
			{
				int x=q[i][front++];
				for(auto y: edge[x])
				{
					if(!y.second&&d[y.first]==-1)
					{
						d[y.first]=d[x];
						q[i].push_back(y.first); //距离在同一层存在同一个q[i]中!!
						rear++;
					}
				}
			}
			if(d[fy]!=-1) break;
			for(int j=0;j<=rear;j++)//细节并不是从front 开始而是从头到尾全部重新走一遍!!
			{
				int x=q[i][j];
				for(auto y:edge[x])
				{
					if(y.second&&d[y.first]==-1)
					{
						d[y.first]=d[x]+1;
						q[i+1].push_back(y.first);//距离加1了在下一层
					}
				}
			}
		}
		printf("%d\n",d[fy]);
	}
}
posted @ 2023-03-25 20:29  _Lance  阅读(54)  评论(0编辑  收藏  举报