洛谷:P2865
WOJ:2240
描述
贝西所在的牧场一共有 N 个地点。 M 条双向通行的道路连接这些地点,其中第 i 条道路连接 Ai 和 Bi,长度为 Li。贝西想从第一个地点走到第 N 个地点,由于路上风景不错,她决定不走最短 路径,而选择次短路径。次短路径的长度严格大于最短路径。如果有两条路径的长度都是最短的,那 么它们都不算次短路径。次短路径允许重复通过一些道路或地点。请你帮助贝西找出次短路径的长度 吧,输入数据保证次短路径一定存在
输入
第一行:两个整数 N 和 M, 1 ≤ N ≤ 5000, 1 ≤ M ≤ 10^5 • 第二行到第 M + 1 行:第 i + 1 行有三个整数 Ai, Bi 和 Li, 1 ≤ Ai; Bi ≤ N; 1 ≤ Li ≤ 5000
输出
单个整数:表示从第一个点到最后一个点的次短路径长度
样例输入
4 4
1 2 100
2 4 200
2 3 250
3 4 100
样例输出
450
提示
最短路是 1 → 2 → 4,总长度为 300,次短路 是 1 → 2 → 3 → 4,总长度为 450
思路
就是一个裸次短路,我在翻题解时,最多的思想就是正反跑一遍SPFA,确定最短路径,再枚举每一条边,当其不包含于最短路径时,更新次短路。
而我就比较另类了,只跑了一次SPFA,在此期间同时维护最短路和次短路,对于每条枚举的边,都要进行以下操作:
- 当u的最短路可以更新v的最短路时,v的次短路更新为v更新前的最短路,在更新v的最短路
- 当u的最短路无法更新v的最短路时,尝试用u的最短路更新v的次短路
- 尝试用u的次短路更新v的次短路
另外,本蒟蒻跑SPFA时是正向跑的,建议反向跑
CODE
#include<bits/stdc++.h>//正向跑SPFA
using namespace std;
const int N=500005;
struct fjy{
int nxt,v,w;
}e[N<<1];
int read(){
int s=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') f=-f;
ch=getchar();
}
while(isdigit(ch)){
s=(s<<3)+(s<<1)+ch-'0';
ch=getchar();
}
return s*f;
}
int first[N],dis[N],dis1[N],val[N];
int cnt,n,m,ui,vi,wi;
void add(int u,int v,int w){
e[++cnt].v=v;e[cnt].nxt=first[u];first[u]=cnt;e[cnt].w=w;
}
queue<int> q;
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++){
dis[i]=dis1[i]=0x7f7f7f7f;
}
for(int i=1;i<=m;i++){
ui=read(),vi=read(),wi=read();
add(ui,vi,wi),add(vi,ui,wi);
}
/*for(int i=first[n];i;i=e[i].nxt){
q.push(e[i].v);val[e[i].v]=1;
dis[e[i].v]=e[i].w;
}//printf("%d;\n",dis1[2]);*/
q.push(1);val[1]=1;
dis[1]=0;
while(!q.empty()){
int v=q.front();q.pop();val[v]=0;//printf("v=%d,disv=%d\n",v,dis[v]);
for(int i=first[v];i;i=e[i].nxt){
if(dis[e[i].v]>dis[v]+e[i].w){//能更新最短路
dis1[e[i].v]=dis[e[i].v];
dis[e[i].v]=e[i].w+dis[v];
if(!val[e[i].v]) q.push(e[i].v),val[e[i].v]=1;
//if(e[i].v==n) printf("dis=%d,disu=%d\n",dis[e[i].v],dis[v]);
}
if(dis1[e[i].v]>dis[v]+e[i].w&&dis[v]+e[i].w>dis[e[i].v]){//用最短路更新次短路
dis1[e[i].v]=dis[v]+e[i].w;
if(!val[e[i].v]) q.push(e[i].v),val[e[i].v]=1;
}
if(dis1[e[i].v]>dis1[v]+e[i].w/*&&dis1[v]+e[i].w>dis[e[i].v]*/)//判断能否用v节点的次短路更新次短路
{
//printf("dis=%d,dis1=%d\n",dis[e[i].v],dis1[e[i].v]);
dis1[e[i].v]=dis1[v]+e[i].w;
if(!val[e[i].v]) q.push(e[i].v),val[e[i].v]=1;
}
}
}
printf("%d\n",dis1[n]);
return 0;
}
#include<bits/stdc++.h>//反向跑SPFA
using namespace std;
const int N=500005;
struct fjy{
int nxt,v,w;
}e[N<<1];
int read(){
int s=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') f=-f;
ch=getchar();
}
while(isdigit(ch)){
s=(s<<3)+(s<<1)+ch-'0';
ch=getchar();
}
return s*f;
}
int first[N],dis[N],dis1[N],val[N];
int cnt,n,m,ui,vi,wi;
void add(int u,int v,int w){
e[++cnt].v=v;e[cnt].nxt=first[u];first[u]=cnt;e[cnt].w=w;
}
queue<int> q;
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++){
dis[i]=dis1[i]=0x7f7f7f7f;
}
for(int i=1;i<=m;i++){
ui=read(),vi=read(),wi=read();
add(ui,vi,wi),add(vi,ui,wi);
}
/*for(int i=first[n];i;i=e[i].nxt){
q.push(e[i].v);val[e[i].v]=1;
dis[e[i].v]=e[i].w;
}//printf("%d;\n",dis1[2]);*/
q.push(n);val[n]=1;
dis[n]=0;//反向时初始化n节点,即以n为起点
while(!q.empty()){
int v=q.front();q.pop();val[v]=0;//printf("v=%d,disv=%d\n",v,dis[v]);
for(int i=first[v];i;i=e[i].nxt){
if(dis[e[i].v]>dis[v]+e[i].w){//能更新最短路
dis1[e[i].v]=dis[e[i].v];
dis[e[i].v]=e[i].w+dis[v];
if(!val[e[i].v]) q.push(e[i].v),val[e[i].v]=1;
//if(e[i].v==n) printf("dis=%d,disu=%d\n",dis[e[i].v],dis[v]);
}
if(dis1[e[i].v]>dis[v]+e[i].w&&dis[v]+e[i].w>dis[e[i].v]){
dis1[e[i].v]=dis[v]+e[i].w;
if(!val[e[i].v]) q.push(e[i].v),val[e[i].v]=1;
}
if(dis1[e[i].v]>dis1[v]+e[i].w/*&&dis1[v]+e[i].w>dis[e[i].v]*/)//判断能否用v节点的次短路更新次短路
{
//printf("dis=%d,dis1=%d\n",dis[e[i].v],dis1[e[i].v]);
dis1[e[i].v]=dis1[v]+e[i].w;
if(!val[e[i].v]) q.push(e[i].v),val[e[i].v]=1;
}
}
}
printf("%d\n",dis1[1]);//n为起点,则1为终点
return 0;
}
错题分析
错误代码:
#include<bits/stdc++.h>
using namespace std;
const int N=500005;
struct fjy{
int nxt,v,w;
}e[N<<1];
int read(){
int s=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') f=-f;
ch=getchar();
}
while(isdigit(ch)){
s=(s<<3)+(s<<1)+ch-'0';
ch=getchar();
}
return s*f;
}
int first[N],dis[N],dis1[N],val[N];
int cnt,n,m,ui,vi,wi;
void add(int u,int v,int w){
e[++cnt].v=v;e[cnt].nxt=first[u];first[u]=cnt;e[cnt].w=w;
}
queue<int> q;
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++){
dis[i]=dis1[i]=0x7f7f7f7f;
}
for(int i=1;i<=m;i++){
ui=read(),vi=read(),wi=read();
add(ui,vi,wi),add(vi,ui,wi);
}
for(int i=first[n];i;i=e[i].nxt){
q.push(e[i].v);val[e[i].v]=1;
dis[e[i].v]=e[i].w;
}//printf("%d;\n",dis1[2]);
/*q.push(1);val[1]=1;
dis[1]=0;*/
while(!q.empty()){
int v=q.front();q.pop();val[v]=0;//printf("v=%d,disv=%d\n",v,dis[v]);
for(int i=first[v];i;i=e[i].nxt){
if(dis[e[i].v]>dis[v]+e[i].w){//能更新最短路
dis1[e[i].v]=dis[e[i].v];
dis[e[i].v]=e[i].w+dis[v];
if(!val[e[i].v]) q.push(e[i].v),val[e[i].v]=1;
//if(e[i].v==n) printf("dis=%d,disu=%d\n",dis[e[i].v],dis[v]);
}
if(dis1[e[i].v]>dis[v]+e[i].w&&dis[v]+e[i].w>dis[e[i].v]){
dis1[e[i].v]=dis[v]+e[i].w;
if(!val[e[i].v]) q.push(e[i].v),val[e[i].v]=1;
}
if(dis1[e[i].v]>dis1[v]+e[i].w/*&&dis1[v]+e[i].w>dis[e[i].v]*/)//判断能否用v节点的次短路更新次短路
{
//printf("dis=%d,dis1=%d\n",dis[e[i].v],dis1[e[i].v]);
dis1[e[i].v]=dis1[v]+e[i].w;
if(!val[e[i].v]) q.push(e[i].v),val[e[i].v]=1;
}
}
}
printf("%d\n",dis1[n]);
return 0;
}
初始化时,并未初始化1号节点,只是初始化了与1号节点相连的其他点的最短路,至于为啥错,本蒟蒻水平低,还没找出来,不喜勿喷