洛谷P2865 [USACO06NOV] Roadblocks G【C++解法】【次短路问题】

本文详细介绍了如何使用SPFA算法求解求次短路问题,通过构建邻接表和维护d1和d2数组来记录最短路径,找出除了最短路径外的次短距离。
摘要由CSDN通过智能技术生成

/*求次短路问题 【spfa解法】 
本题思路:1.用spfa做,用d1记录从1到n所有点距离点1的最短距离,
                    用d2记录从n到1所有点距离点n的最短距离
                    那么此时d1[n]即为1到n点的最短距离
                 2.遍历每个顶点x,找到它们所指向的点y,利用d1[x] (x距离1的最短距离)  + d2[y](y距·                  离n的最短距离) + w[i] (x和y的边的权值)
                   因为次短路一定严格大于最短路,而且又是除了最短路以外最小的那个,
                   所以利用 
                   int temp=d1[t]+d2[j]+w[i];
                   if(temp>d1[n] && temp<ans) ans=temp;
                  每次找到大于最短路,但又是最小的那个,
                3.输出答案即可 
*/


#include<bits/stdc++.h>
using namespace std;
const int N = 5010,M=1e6+10;
//建立邻接表 
int h[N],e[M],w[M],ne[M],idx;
int n,m;
//d1记录从1到n所有点距离点1的最短距离
//d2记录从n到1所有点距离点n的最短距离
int d1[N],d2[N]; 
bool st[N];//st数组记录是否在队列中,true代表在,false代表不在,避免重复的点同时重复进入队列中 

//运用头插法 
void add(int a,int b,int c)
{//e记录节点编号:w记录(a指向b的)边权值:ne记录指针,h记录a的头节点 
	e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
/*
	spfa算法思路:
	1.从第一个点出发,直到遍历完所有点 
	2.每次只向外扩展一层;
	3.其实是Bellman_ford的优化,不过spfa算法不适合限制最多只能走k步的最短路 
*/ 

//从1到n,找到所有距离点1的最短路 
void spfa1()
{
	//队列q 
	queue<int> q;
	memset(d1,0x3f,sizeof(d1));//初始化d1数组每个变成正无穷(0x3f3f3f3f) 
	d1[1]=0;//点1距离初始化为1 
	q.push(1);//点1进入队列 
	st[1]=true;//点1在队列中 
	
	while(q.size())
	{
		//取出队头元素并弹出 
		auto t=q.front();
		q.pop();
		st[t]=false;//表示此时点t不在队列中 
		
		//遍历点t所指向的其他点 
		for(int i=h[t];i!=-1;i=ne[i])
		{
			//取出被指向点的编号 
			int j=e[i];
			if(d1[j]>d1[t]+w[i])//如果可以更新其距离,则进入 
			{
				d1[j]=d1[t]+w[i];//并修改 
				
				//这里判断是防止,多条重边出现,那么t可能遍历多个相同的j,
				//而可能后面的j的w[i]值会更小就会不断更新d[j]而进入if(d1[j]>d1[t]+w[i])这个条件中
				//那么为了防止队列中重复记录相同元素,所以压入队列元素需要判断条件 if(!st[j])
				if(!st[j])
				{
					q.push(j);
					st[j]=true;//说明点j进入队列 
				} 
			}
		}
	}
}

//spfa2和上面的spfa1其实思路一样,不同的是这次反过来跑图
//d2记录了从点n到点1所有 距离点n 的最短距离!!!
//看懂spfa1,spfa2就简单啦,我这就不多赘述了。 
void spfa2()
{
	queue<int> q;
	memset(d2,0x3f,sizeof(d2));
	memset(st,false,sizeof(st));
	d2[n]=0;
	q.push(n);
	st[n]=true;
	
	while(q.size())
	{
		auto t=q.front();
		q.pop();
		st[t]=false;
		
		for(int i=h[t];i!=-1;i=ne[i])
		{
			int j=e[i];
			if(d2[j]>d2[t]+w[i])
			{
				d2[j]=d2[t]+w[i];
				if(!st[j])
				{
					q.push(j);
					st[j]=true;
				} 
			}
		}
	}
}

//进入主函数 
int main()
{
	//初始化所有头节点,都指向-1 
	memset(h,-1,sizeof(h));
	
	//输入 
	cin>>n>>m;
	for(int i=0;i<m;i++)
	{
		int a,b,c;
		cin>>a>>b>>c;
		//因为是双向可以走,所以就每两个点建两条边 1.a指向b  2. b指向a 
		add(a,b,c);
		add(b,a,c);
	}	
	
	//核心代码 
	spfa1();
	spfa2();
	
	
	//ans记录次短路答案 
	int ans=1e9;
	for(int t=1;t<=n;t++)//遍历每个点 
	{
		for(int i=h[t];i!=-1;i=ne[i])//遍历每个点指向的每个节点 
		{
			int j=e[i];
			int temp=d1[t]+d2[j]+w[i];// 
			if(temp>d1[n] && temp<ans) ans=temp;//既要严格大于最短距离d1[n],又要是其他大于最短距离的距离中取最小值	
		} 
	}
	
	//输出答案 
	cout<<ans<<endl;
	
	return 0;
}

  • 8
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

#Dong#

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值