洛谷 P2296 寻找道路

题目描述

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

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

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

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

输入输出格式

输入格式:

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

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

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

输出格式:

输出只有一行,包含一个整数,表示满足题目描述的最短路径的长度。如果这样的路径不存在,输出 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:

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

解释2:

如上图所示,满足条件的路径为 1 - > 3 - > 4 - > 5 。注意点 2 不能在答案路径中,因为点 2 连了一条边到点 6 ,而点 6 不与终点 5 连通。

【数据范围】

对于 30\%30% 的数据, 0 < n \le 100<n10 , 0 < m \le 200<m20 ;

对于 60\%60% 的数据, 0 < n \le 1000<n100 , 0 < m \le 20000<m2000 ;

对于 100\%100% 的数据, 0 < n \le 10000, 0 < m \le 200000,0 < x,y,s,t \le n, x,s \ne t0<n10000,0<m200000,0<x,y,s,tn,x,st 。

列下此题要解决的问题

1.自环

2.如何判断一条路线上的点的所有出度都直接或间接的指向重点

1比较好解决,读入时加一句if(a==b)continue就好了,那如何解决2呢

从起点出发的话对于途径的每一个点都要判断是否可以直接/间接到终点,计算量太大,所以考虑反过来走,从终点出发,那么在此之前,连边时就要把所有的边反过来连,然后从终点出发往起点走

这样对不对呢,我们来证明一下

样例2原图

以样例2为例,反向连边后如上图,我们从5出发,可以走5->4->3->1,也可以走5->2->1,点6无论怎样都不会被走到,所以我们可以把点6从图中删除

但是这样的话2这个不合法点不就可以走了吗?所以我们在删除一个点时,还要删掉所有它能走到的点,因为它们也是不合法的

最后图中剩下的路径就是合法的了,对于一个合法的图求最短路,跑一边bfs即可,如果bfs的结果为0,说明走不到,输出-1即可

p.s还是从终点往起点走

完整代码

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+10;
int n,m,x,y,qd,zd,ans[MAXN];
vector<int>g[MAXN];
queue<int>q;
bool vis[MAXN],can[MAXN];
int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++)
       {
        cin>>x>>y;
        if(x==y)continue;//去自环
        g[y].push_back(x);    
            }
    cin>>qd>>zd;
    can[zd]=1;//从终点开始走
    q.push(zd);
    while(!q.empty())//第一遍排除所有不合法的点,它们不会被标记
    {
    int no=q.front();q.pop();    
    for(int i=0;i<g[no].size();i++)
        if(!can[g[no][i]])
           can[g[no][i]]=1,q.push(g[no][i]);       
        }
    memcpy(vis,can,sizeof(can));//复制一个同样的标记数组,因为在第一个标记数组中删除不合法点是有后效性的
    for(int i=1;i<=n;i++)//把所有不合法点能走到的点全部删除
       if(!can[i])
          for(int j=0;j<g[i].size();j++)
             if(vis[g[i][j]])
                 vis[g[i][j]]=0;
    q.push(zd);//还是从终点开始走
    while(!q.empty())//bfs
    {
    int no=q.front();    
    q.pop();    
    for(int i=0;i<g[no].size();i++)
       if(vis[g[no][i]])
          {
            q.push(g[no][i]);    
            vis[g[no][i]]=0;    
            ans[g[no][i]]=ans[no]+1;    
                }    
        
        }
    if(ans[qd]==0)cout<<-1;
    else cout<<ans[qd];//快乐的输出                             
    return 0;
} 

参考大佬@ _空_ 的题解

猜你喜欢

转载自www.cnblogs.com/pcpcppc/p/9339212.html