[USACO06NOV]路障Roadblocks

洛谷: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号节点相连的其他点的最短路,至于为啥错,本蒟蒻水平低,还没找出来,不喜勿喷

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值