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

/*求次短路问题 【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                   每次找到大于最短路,但又是最小的那个,
                3.输出答案即可 
*/


#include
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 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 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>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

你可能感兴趣的:(c++,算法,数据结构,图论)