参考链接
解题过程
- 读入连接关系,注意这里有坑,卡10分,具体坑看代码中注释。
- 预处理小路连接,这里使用floyd算法对小路连小路进行合并,主要目的就是在使用spfa算法求解时不用再考虑小路连小路的情况,因为如果在使用spfa算法时考虑小路连小路则难以计算疲劳值。
- 使用spfa求解最小疲劳值,先说一下spfa算法,它的思路和Dijkstra是相似的。Dijkstra算法的思想就是不断地在距离矩阵中寻找离源节点最近的点作为中间接节点,然后尝试使用中间节点去更新其他节点到源节点的距离,整个过程用visit数组做标记,标记该点是否已被访问,所以每个点只被选择一次。而spfa算法的思路略有不同,下面列出论文中的算法描述以及形式算法描述。
SPFA算法采用图的存储结构是邻接表,方法是动态优化逼近法。算法中设立了一个先进先出的队列Queue用来保存待优化的顶点 ,优化时从此队列里顺序取出一个点w,并且用w点的当前路径D[w]去优化调整其它各点的路径值D[j],若有调整,即 D[j]的值改小了,就将 j点放入Queue队列以待继续进一步优化。反复从Queue队列里取出点来对当前最短路径进行优化,直至队空不需要再优化为止,此时D数组里就保存了从源点到各点的最短路径值 。
- 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;
}