洛谷 P2296 寻找道路(最短路) 题解

94 篇文章 0 订阅

题目来源:

https://www.luogu.org/problemnew/show/P2296

题目描述:

题目描述

在有向图 GG 中,每条边的长度均为 11,现给定起点和终点,请你在图中找一条从起点到终点的路径,该路径满足以下条件:

  1. 路径上的所有点的出边所指向的点都直接或间接与终点连通。
  2. 在满足条件11的情况下使路径最短。

注意:图 GG 中可能存在重边和自环,题目保证终点没有出边。

请你输出符合条件的路径的长度。

输入输出格式

输入格式:

 

第一行有两个用一个空格隔开的整数 nn 和 mm,表示图有 nn 个点和 mm 条边。

接下来的 mm 行每行 22 个整数 x,yx,y,之间用一个空格隔开,表示有一条边从点 xx 指向点yy。

最后一行有两个用一个空格隔开的整数 s, ts,t,表示起点为 ss,终点为 tt。

 

输出格式:

 

输出只有一行,包含一个整数,表示满足题目描述的最短路径的长度。如果这样的路径不存在,输出-1−1。

 

输入输出样例

输入样例#1: 复制

3 2  
1 2  
2 1  
1 3  

输出样例#1: 复制

-1

输入样例#2: 复制

6 6  
1 2  
1 3  
2 6  
2 5  
4 5  
3 4  
1 5  

输出样例#2: 复制

3

说明

解释1:

如上图所示,箭头表示有向道路,圆点表示城市。起点11与终点33不连通,所以满足题目描述的路径不存在,故输出-1−1 。

解释2:

如上图所示,满足条件的路径为11- >33- >44- >55。注意点22 不能在答案路径中,因为点22连了一条边到点66 ,而点66不与终点55 连通。

【数据范围】

对于30\%30%的数据,0 < n \le 100<n≤10,0 < m \le 200<m≤20;

对于60\%60%的数据,0 < n \le 1000<n≤100,0 < m \le 20000<m≤2000;

对于100\%100%的数据,0 < n \le 10000, 0 < m \le 200000,0 < x,y,s,t \le n, x,s \ne t0<n≤10000,0<m≤200000,0<x,y,s,t≤n,x,s≠t。

解题思路:

       本身就是一个最短路,但是因为对最短路径上的点有限制,所以麻烦一点,我先反向建边,处理出终点可以到的点,然后在处理每一个点的出边能否到终点,然后只要在求最短路的时候判断能不能使用就行。。

代码:

#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include <queue>
#include <stack>
#include <cmath>
#define inf 0x3f3f3f3f
#define ll long long
using namespace std;
const int  maxn=2*1e5+10;
int n,m,head[10010],vis[10010],dis[10010],pd[10010],jl[10010],cnt=0,S,T;
struct newtt
{
    int from,to,cost;
}edge[maxn];
struct newt
{
    int to,next,cost;
}e[maxn];
void addedge(int u,int v,int w)
{
    e[cnt].to=v;
    e[cnt].cost=w;
    e[cnt].next=head[u];
    head[u]=cnt++;
}
void dfs(int u)
{
    pd[u]=1;
    for(int i=head[u];i!=-1;i=e[i].next)
    {
        int v=e[i].to;
        if(!pd[v])dfs(v);
    }
    return; 
}
void cl(int u)
{
    vis[u]=1;
    int flag=1;
    for(int i=head[u];i!=-1;i=e[i].next)
    {
        int v=e[i].to;
        if(!pd[v])flag=0;
        if(!vis[v])cl(v);
    }
    jl[u]=flag;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)head[i]=-1;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&edge[i].from,&edge[i].to);
        if(edge[i].from==edge[i].to)continue;
        addedge(edge[i].to,edge[i].from,1);
    }
    scanf("%d%d",&S,&T);
    dfs(T);
    //for(int i=1;i<=n;i++)printf("%d\n",pd[i]);
    for(int i=1;i<=n;i++)head[i]=-1,dis[i]=1e9;
    cnt=0;
    for(int i=1;i<=m;i++)
    addedge(edge[i].from,edge[i].to,1);
    for(int i=1;i<=n;i++)
    if(!vis[i])cl(i);
    for(int i=1;i<=n;i++)vis[i]=0;
    queue<int>q;
    vis[S]=1;
    q.push(S);
    dis[S]=0;
    while(!q.empty())
    {
        int now=q.front();q.pop();
        vis[now]=0;
        for(int i=head[now];i!=-1;i=e[i].next)
        {
            int v=e[i].to;
            if(!jl[v])continue;
            if(dis[v]>dis[now]+e[i].cost)
            {
                dis[v]=dis[now]+e[i].cost;
                if(vis[v])continue;
                vis[v]=1;
                q.push(v);
            }
        }
    }
//	for(int i=1;i<=n;i++)printf("%d %d\n",pd[i],jl[i]);
    if(dis[T]==1e9)puts("-1");
    else printf("%d\n",dis[T]);
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值