刷题记录:牛客NC16498寻找道路

传送门:牛客

题目描述:

在有向图G中,每条边的长度均为1,现给定起点和终点,请你在图中找一条从起点到终点的路径,该路径满足以下条件:
1.路径上的所有点的出边所指向的点都直接或间接与终点连通。
2.在满足条件1的情况下使路径最短。
注意:图G中可能存在重边和自环,题目保证终点没有出边。
请你输出符合条件的路径的长度。
输入:
3 2
1 2
2 1
1 3
输出:
-1

这道题的思维难度感觉还是有的,但是解法也比较经典,做起来比较??`

主要思路:

  1. 首先我们会发现这道题跟我们的最短路算法肯定是有一定的关系的.但是对于这道题我们不仅需要解决最短路的问题,我们还是有一些限制的,比如路径上的点的出边所指向的点都与终点相通
  2. 所以我们要如何解决这个问题呢.我们可以使用建反边的方法.试想一下,假设我们将边反向然后求出终点到每一个的点是否相同,因为是反边的原因,那么原来的情况就是那些点都指向我们的终点.是不是就是限制我们的条件了.然后我们只要枚举每一个不指向终点的点(此时还是在反边的图中枚举,),假设每一个点与我们与终点相通的点相通的话,那么这些与终点相通的点就是不满足我们的条件的(因为是反边,所以反过来的话这些点指向的肯定是我们的不满足的点),然后将这些不满足的点删掉即可
  3. 这里可能有人会有疑问,为什么不枚举满足的点的边呢,因为此时我们建立的是反边枚举满足的点的话是不满足的点指向自己(和题意刚好相反.

注意:注意自环的问题,需要特判.注意重边的问题,在跑第一次BFS时注意去掉(也就是已经跑过的点就不用再跑了),不然会TLE+MLE!!.

在下方跑最短路时我直接套上了Dij,但是此题SPFA甚至BFS都是可以过的(但是建议不会dij的学习一下堆优化版的Dij)

下面是具体的代码部分:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <string.h>
#include <stack>
#include <deque>
using namespace std;
typedef long long ll;
#define inf 0x3f3f3f3f
#define root 1,n,1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
	ll x=0,w=1;char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}
#define maxn 200006
#define ll_maxn 0x3f3f3f3f3f3f3f3f
const double eps=1e-8;
struct Edge{
	int u,v,dist;
};
vector<Edge>edge[maxn];
struct heapnode{
	int d,u;
	bool operator<(const heapnode &rhs) const {
		return d>rhs.d;
	}
};
int n,m;
int dis[maxn],vis[maxn];int vis2[maxn];
void dij(int s) {
	priority_queue<heapnode>q;
	for(int i=1;i<=n;i++) dis[i]=inf;
	dis[s]=0;
	q.push({0,s});
	while(!q.empty()) {
		heapnode f=q.top();q.pop();
		int u=f.u;
		if(vis2[u]==0) continue;
		vis2[u]=0;
		for(int i=0;i<edge[u].size();i++) {
			int v=edge[u][i].v;
			if(dis[v]>dis[u]+edge[u][i].dist) {
				dis[v]=dis[u]+edge[u][i].dist;
				q.push({dis[v],v});
			}
		}
	}
}
int main() {
	memset(vis,0,sizeof(vis));
	memset(vis2,0,sizeof(vis2));
	n=read();m=read();int x,y;
	for(int i=1;i<=m;i++) {
		x=read();y=read();
		if(x==y) continue;
		edge[y].push_back({y,x,1});
	}
	int s,t;s=read();t=read();
	queue<int>q;
	q.push(t);
	vis[t]=1;
	while(!q.empty()) {
		int f=q.front();q.pop();
		for(int i=0;i<edge[f].size();i++) {
			if(!vis[edge[f][i].v]) {
				vis[edge[f][i].v]=1;
				q.push(edge[f][i].v);
			}
		}
	}
	memcpy(vis2,vis,sizeof(vis));
	for(int i=1;i<=n;i++) {
		if(vis[i]==0) {
			for(int j=0;j<edge[i].size();j++) {
				if(vis2[edge[i][j].v]) {
					vis2[edge[i][j].v]=0;
				}
			}
		}
	}
	vis2[t]=1;
	dij(t);
	if(dis[s]==inf) printf("-1\n");
	else printf("%d\n",dis[s]);
 	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值