与其留恋,不如洒脱。 —NEMT2018后的首题祭
行路难(SPFA+前驱记录)
题目来自洛谷P2832,难度(提高+/省选-),自以为应该是(普及/提高-)的黄题。
题目概述:给定一张有向图,在图上每进行一次移动,图的所有边权都会在原基础上+1,求算从1到N点的最小边权和,并输出这个最短路。
思路:比较显然的一件事情是,这个是个图论的最短路问题,还要记录前驱。每次移动都需要在上一次边权的基础上+1,所以我们考虑最短路的通用算法SPFA,大概这就是模板吧,再加上一个时间数组。
Q:那我和节点一样,也开一个时间队列queue< int >t可以嘛?
A:不可以的,考虑一下,如果加上这样一个队列,那就意味着你的t和q要同时进行操作,换句话说,q执行什么操作,t也得跟着操作。考虑一种比较普遍的情况,假设到达GOAL节点可以从A或者B点出发,而从A走累计距离长但我们先遍历,从B走累计距离短但我们后遍历,这样的话到达GOAL节点的时间到底算是从A还是从B呢。(好吧我表达能力差,意思大概就是这样不行,而且这样做只能得40分,就是这一段)
错误的SPFA
int spfa()
{
int x,son,ti;
while(!q.empty())
{
x=q.front(),ti=t.front();
for(i=hd[x];i;i=a[i].nxt)
{
son=a[i].v;
if(dis[son]>dis[x]+a[i].w+ti)
{
q.push(son);
t.push(ti+1);
dis[son]=dis[x]+a[i].w+ti;
pre[son]=x;
}
}
q.pop(),t.pop();
}
}
正解
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<queue>
using namespace std;
int i,j,n,m;
int hd[10001],pre[10001],ans[10001],t[10001];
struct data
{
int v,w,nxt;
}a[200001];
int r()
{
int ans=0;
char ch=getchar();
while(ch<'0'||ch>'9')
{
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
ans*=10;
ans+=ch-'0';
ch=getchar();
}
return ans;
}
int xx,yy,zz,temp;
void add(int x,int y,int z)
{
a[++temp].v=y;
a[temp].w=z;
a[temp].nxt=hd[x];
hd[x]=temp;
}
queue<int>q;
int dis[10001];
int spfa()
{
int x,son;
while(!q.empty())
{
x=q.front();
for(i=hd[x];i;i=a[i].nxt)
{
son=a[i].v;
if(dis[son]>dis[x]+a[i].w+t[x])
{
q.push(son);
t[son]=t[x]+1;
dis[son]=dis[x]+a[i].w+t[x];
pre[son]=x;
}
}
q.pop();
}
}
int main()
{
n=r(),m=r();
for(i=1;i<=m;i++)
{
xx=r(),yy=r(),zz=r();
add(xx,yy,zz);
}
memset(dis,0x7f7f7f,sizeof(dis));
dis[1]=0;
q.push(1);
spfa();
cout<<dis[n]<<endl;
j=n;
int tot=0;
while(j!=1)
{
ans[++tot]=j;
j=pre[j];
}
cout<<1;
for(i=tot;i>=1;i--)
{
cout<<" "<<ans[i];
}
return 0;
}
/*
5 7
1 5 7
1 2 1
1 4 1
3 5 1
2 3 1
2 4 1
4 5 1
*/
上图,传统不能丢。
今天是可爱的埃罗芒阿老师(划掉)和泉纱雾哦