洛谷【P2865】[USACO06NOV]路障Roadblocks

题目链接

这是一道次短路模板题。作为一名 SPFA 信仰选手我决定用 SPFA 来解决这道题。




首先呢,思路还算简单,整个程序就是分别从节点 1 和 n 跑一遍最短路,然后进行判断。

如何判断,是本题的重点。

我们可以枚举每一个点分别到 1 和 n 的最短路之和,对所有点求一个次小值(最小值就是最短路),就是次短路,即:

d i s 1 [ n ] < d i s 1 [ i ] + d i s 2 [ i ] dis1[n]<dis1[i]+dis2[i] dis1[n]<dis1[i]+dis2[i]

但这样只能得50分,因为它有一个漏洞,那就是自环。对于自环,上式就相当于取自环中最短的一条边进行判断,如果不满足条件则放弃这个点。但如果这条最短的边恰巧是最短路,而自环中又恰巧有比它稍微大一点点的边,成为次短路的一部分,那么,它就被忽略了。

所以,要按每一条边进行枚举

怎么枚举呢?要知道,判断最短路时是通过松弛:

d i s [ v ] > m a p [ k ] . w + d i s [ u ] dis[v] > map[k].w+dis[u] dis[v]>map[k].w+dis[u]

来判断,那么,次短路亦可以用类似的办法,即:

d i s 1 [ n ] < d i s 1 [ i ] + d i s 2 [ v ] + m a p [ k ] . w dis1[n]<dis1[i]+dis2[v]+map[k].w dis1[n]<dis1[i]+dis2[v]+map[k].w

对于每个点 i,枚举与它相连的所有点之间的所有边,判断这个值与最短路的关系,这样就不会有遗漏了。

这样跑下来时间复杂度应该是 O(nm) 的,看一眼数据范围,哎嘛舒服,直接过了。




C++:

#include <stdio.h>
#include <queue>
#include <iostream>
#include <string.h>
using namespace std;
#define MAXN 100005

int m,n,cnt;
int dis1[MAXN],dis2[MAXN],vis[MAXN],head[MAXN*2];

queue <int>que;

struct node{
	int to,next,w;
}map[MAXN*2];

void add(int u,int v,int w){
	map[++cnt] = (node){v,head[u],w};
	head[u] = cnt;
}
void SPFA(int u,int *dis){
	memset(vis,0,sizeof(vis));
	que.push(u);
	dis[u] = 0;
	vis[u] = 1;
	while(!que.empty()){
		int u = que.front(); que.pop();
		vis[u] = 0;
		for(int k=head[u];k;k=map[k].next){
			int v = map[k].to;
			int w = map[k].w+dis[u];
			if( dis[v] > w){
				dis[v] = w;
				if(!vis[v]){
					vis[v] = 1;
					que.push(v);
				}
			}
		}
	}
}
int main(void){
    memset(dis1,0x3f,sizeof(dis1));
    memset(dis2,0x3f,sizeof(dis2));
	cin >> n >> m;
	for(int i=1;i<=m;i++){
		int u,v,w;
		cin >> u >> v >> w;
		add(u,v,w);	add(v,u,w);
	}
	SPFA(1,dis1); SPFA(n,dis2);
	int ans=0x3f3f3f3f;
	for(int i=1;i<=n;i++){
		for(int k=head[i];k;k=map[k].next){
			int v = map[k].to;
			int w = dis1[i]+dis2[v]+map[k].w;
			if(dis1[n]<w && ans>w)	ans = w;
		}
	}
	cout << ans << endl;
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SP FA

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值