CCF 201712-4 试题名称: 行车路线

参考链接

参考博客链接
spfa论文链接

解题过程

  1. 读入连接关系,注意这里有坑,卡10分,具体坑看代码中注释。
  2. 预处理小路连接,这里使用floyd算法对小路连小路进行合并,主要目的就是在使用spfa算法求解时不用再考虑小路连小路的情况,因为如果在使用spfa算法时考虑小路连小路则难以计算疲劳值。
  3. 使用spfa求解最小疲劳值,先说一下spfa算法,它的思路和Dijkstra是相似的。Dijkstra算法的思想就是不断地在距离矩阵中寻找离源节点最近的点作为中间接节点,然后尝试使用中间节点去更新其他节点到源节点的距离,整个过程用visit数组做标记,标记该点是否已被访问,所以每个点只被选择一次。而spfa算法的思路略有不同,下面列出论文中的算法描述以及形式算法描述。

SPFA算法采用图的存储结构是邻接表,方法是动态优化逼近法。算法中设立了一个先进先出的队列Queue用来保存待优化的顶点 ,优化时从此队列里顺序取出一个点w,并且用w点的当前路径D[w]去优化调整其它各点的路径值D[j],若有调整,即 D[j]的值改小了,就将 j点放入Queue队列以待继续进一步优化。反复从Queue队列里取出点来对当前最短路径进行优化,直至队空不需要再优化为止,此时D数组里就保存了从源点到各点的最短路径值 。

kHo6YV.png
kHoyF0.md.png
kHoroq.md.png

  1. spfa算法中,使用两个距离矩阵进行计算,dis[wide][i][j]表示最后一条路为大路的i,j间的最小疲劳值,同理dis[narrow][i][j]表示最后一条为小路,分3种情况对dis数组进行更新,大路连大路,大路连小路,小路连大路,前面已经解释了为什么这里没有小路连小路的情况。最后更新完毕后,min(dis[wide][1][n],dis[narrow][1][n])就是想要的答案。
#include <bits/stdc++.h>
#define MAXN 505
using namespace std;

typedef long long ll;

int n,m; 
ll G[2][MAXN][MAXN];      
bool inque[MAXN];
ll dis[2][MAXN];         

const ll inf = 1e18;
const int narrow = 1;
const int wide = 0;

void spfa(int start,int n)
{ 
	for(int i = 1;i <= n; i++)
	{
		dis[narrow][i] = dis[wide][i] = inf;
		inque[i] = false;
	}
	
	queue<int> q;
	q.push(start);
	
	dis[narrow][start] = 0;
	dis[wide][start] = 0;
	inque[start] = true;    // id为start的点入队列 
	
	while(!q.empty())
	{
		int u = q.front();
		q.pop();
		
		inque[u] = false;        //id为u的点出队列 
		
		for(int i = 1;i <= n; i++)
		{
			ll v = G[wide][u][i];
			if(dis[wide][i] > dis[wide][u] +  v)     //大路后接大路 
			{
				dis[wide][i] = dis[wide][u] + v;
				if(!inque[i])
				{
					q.push(i);
					inque[i] = true;
				}
			}
			if(dis[wide][i] > dis[narrow][u] + v)    //大路后接小路 
			{
				dis[wide][i] = dis[narrow][u] + v;
				if(!inque[i])
				{
					q.push(i);
					inque[i] = true;
				}
			}
			if(G[narrow][u][i] != inf)              //能通过小路连连接
			{
				v = G[narrow][u][i] * G[narrow][u][i];
				if(dis[narrow][i] > dis[wide][u] + v)   //小路连大路
				{
					dis[narrow][i] = dis[wide][u] + v;
					if(!inque[i])
					{
						q.push(i);
						inque[i] = true;
					}
				}
			}
		}
	}
}

int main(int argc, char const *argv[])
{
	cin >> n >> m;
	
	for(int i = 0;i <= n; i++)	
		for(int j = 0;j <= n; j++)
			G[narrow][i][j] = G[wide][i][j] = inf;
	
	ll type,u,v,len;
	for(int i = 0;i < m; i++) 
	{
		cin >> type >> u >> v >> len;
		if(G[type][u][v] > len)                   //这里卡  10分 !!!!! 
			G[type][u][v] = G[type][v][u] = len;
	}
	
	//先用floyd算法计算i,j间小路距离
	for(int i = 1;i <= n; i++) 
	{
		for(int j = i+1;j <= n; j++)
		{
			for(int k = 1;k <= n; k++)
			{
				if(i == k || k == j)   continue;
				if(G[narrow][i][j] > G[narrow][i][k] + G[narrow][k][j])
					G[narrow][i][j] = G[narrow][j][i] = G[narrow][i][k] + G[narrow][k][j];
			}
		}
	}
	
	// spfa 求距离 
	spfa(1,n);
	
	cout << min(dis[narrow][n],dis[wide][n]) << endl;
	
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值