#spfa,离散#poj 3171 jzoj 1254 洛谷 4644 清理牛棚 Cleaning Shifts

没有边权的题解poj 2376


题目

poj 2376带边权版


分析

搜到了一份厉害的解题报告, s p f a spfa spfa,不由得谈最短路色变,可以发现中间点其实是不紧要的,因为如果能跑最短路,那么中间的点一定能被覆盖,关于可能重叠的问题就反向建边权为0的边就行了

  • x − > y x->y x>y
  • y − > x y->x y>x

看起来好像不行,其实画个图就可以解释清楚
这里写图片描述
如图,上图是无法覆盖的,而下图是可以覆盖的,所以反向边就是为了处理重叠部分,讲到这里,搞搞离散跑最短路就好了


代码

#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>
struct node{int x,y,w,next;}e[30001]; std::queue<int>q;
int n,s,t,ls[20001]; std::vector<int>uk;
int in(){
	int ans=0; char c=getchar();
	while (c<48||c>57) c=getchar();
	while (c>47&&c<58) ans=(ans<<3)+(ans<<1)+c-48,c=getchar();
	return ans;
}
int main(){
	n=in(); s=in(); t=in()+1;
	for (register int i=1;i<=n;i++){
		e[i].x=in(); e[i].y=in()+1; e[i].w=in();//完全覆盖(所以多增加1避免没有重复部分且完成(使出现重复部分))
		uk.push_back(e[i].x); uk.push_back(e[i].y);
	}
	std::sort(uk.begin(),uk.end());
	int m=std::unique(uk.begin(),uk.end())-uk.begin();//去重
	for (register int i=0;i<m;i++) ls[i]=66666;
	for (register int i=1;i<=n;i++){
		e[i].x=std::lower_bound(uk.begin(),uk.begin()+m,e[i].x)-uk.begin();
		e[i].y=std::lower_bound(uk.begin(),uk.begin()+m,e[i].y)-uk.begin();//离散
		e[i].next=ls[e[i].x]; ls[e[i].x]=i;//建邻接表
	}
    int flag1=0,flag2=0;
    for (register int i=1;i<m;i++) e[n+i]=(node){i,i-1,0,ls[i]},ls[i]=n+i;//反向边
    for (register int i=0;i<m;i++){
    	if (uk[i]==s) flag1=i+1;//记录是否存在起、终点
    	if (uk[i]==t) flag2=i+1;
    }
    if (!flag1||!flag2) return !printf("-1"); 
    flag1--; flag2--; int dis[m]; bool v[m];
    for (register int i=0;i<m;i++) dis[i]=707406378,v[i]=0;
    q.push(flag1); dis[flag1]=0; v[flag1]=1;
    while (q.size()){//spfa
    	int x=q.front(); q.pop();
    	for (register int i=ls[x];i!=66666;i=e[i].next)
		if (dis[e[i].y]>dis[x]+e[i].w){
    		dis[e[i].y]=dis[x]+e[i].w;
    		if (!v[e[i].y]) v[e[i].y]=1,q.push(e[i].y);
    	}
    	v[x]=0;
    }
    if (dis[flag2]==707406378) return !printf("-1");//找不到最短路
	else return !printf("%d",dis[flag2]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值