洛谷P2832 行路难 分析+题解代码【玄学最短路】
题目背景:
小X来到了山区,领略山林之乐。在他乐以忘忧之时,他突然发现,开学迫在眉睫
题目描述:
山区有n座山。山之间有m条羊肠小道,每条连接两座山,只能单向通过,并会耗费小X一定时间。
小X现在在1号山,他的目的是n号山,因为那里有火车站。
然而小X的体力是有限的。他每通过一条羊肠小道,就会变得更疲劳,导致他通过任意一条羊肠小道的时间都增加1。
输入格式:
第一行两个数,n,m
第2行到第m+1行,每行3个数A,B,C,表示A、B之间有一条羊肠小道,可以让小X花费C的时间从A移动到B
输出格式:
两行 第一行一个数T,表示小X需要的最短时间
第二行若干个数,用空格隔开,表示小X的移动路线
例:1 4 2 5表示:小X从1号山开始,移动到4号山,再到2号山,最后到5号山。
输入样例:
5 8
2 4 2
5 2 1
1 2 1
4 3 2
1 3 3
4 5 2
1 5 8
3 5 3
输出样例:
5 8
2 4 2
5 2 1
1 2 1
4 3 2
1 3 3
4 5 2
1 5 8
3 5 3
算法分析:
很明显这是一道最短路(这不是废话嘛)
不过这道题引入了一个疲劳度
乍一看似乎挺难
但其实只要在更新最短路的时候
记录当前被更新最短路的节点i之前已经走过了几节点
即我们以num[v]表示v之前已经走过了几个节点
num[v]=num[u]+1;
u为能使到达v的最短路更新的上一个中介点
所以我们只要在下一次以v为中介点判断是否能更新最短路时加上num[v]即可
其余就只剩SPFA的模板了
废话不多说,呈上蒟蒻代码
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
int n,m;
struct node
{
int v;
int dis;
};
vector<node> map[1000010];
int d[1000010];
int num[1000010];//记录疲劳值的数组,初始全部为0
bool inq [1000010];
int pre[1000010];//记录最短路径
void print(int u)
{
if(pre[u]==u)
{
cout<<u<<" ";
return ;
}
print(pre[u]);
cout<<u<<" ";
}
void SPFA()
{
for(int i=1;i<=n;i++)
pre[i]=i;
memset(d,127,sizeof(d));
queue<int> q;
q.push(1);
inq[1]=true;
d[1]=0;
while(!q.empty())
{
int u=q.front();
inq[u]=false;
q.pop();
for(int j=0;j<map[u].size();j++)
{
int v=map[u][j].v;
int dis=map[u][j].dis;
if(d[u]+dis+num[u]<d[v])
{
d[v]=d[u]+dis+num[u];
pre[v]=u;
num[v]=num[u]+1;//**高亮**这里更新num[v]的疲劳值
if(!inq[v])
{
q.push(v);
inq[v]=true;
}
}
}
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int s,t,dis;
cin>>s>>t>>dis;
map[s].push_back( (node) {t,dis});
}
SPFA();
cout<<d[n]<<endl;
print(n);
return 0;
}
其实就是在SPFA模板里多加了一行。。。
妥妥的 “提高+/省选-” 玄学最短路