寻找道路 (NOIP)

寻找道路

链接:https://ac.nowcoder.com/acm/problem/16498

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

请你输出符合条件的路径的长度。
输入描述:
第一行有两个用一个空格隔开的整数n和m,表示图有n个点和m条边。
接下来的m行每行2个整数x、y,之间用一个空格隔开,表示有一条边从点x指向点y。
最后一行有两个用一个空格隔开的整数s、t,表示起点为s,终点为t。
输出描述:
输出只有一行,包含一个整数,表示满足题目描述的最短路径的长度。如果这样的路径不存在,输出-1。
示例1
输入

3 2
1 2
2 1
1 3

输出

-1

说明

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

示例2
输入

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

输出

3

说明

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

备注:

对于30%的数据,0< n≤10,0< m≤20;
对于60%的数据,0< n≤100,0< m≤2000;
对于100%的数据,0< n≤10,000,0< m≤200,000,0< x,y,s,t≤n,x≠t。

解题思路

一个有向图求最短路径前提是 : 经过该路径的点必须都直接或间接指向终点
所以我们先反向建图从终点搜索到起点,然后标记该路径上的点
然后再一次搜索,从起点搜索到终点,路径上没加一个点就枚举该点下面连接的点,如果该点状态没有被标记,则这个点不计入路径

错误代码

#include <bits/stdc++.h>
using namespace std;

template<class T>inline void read(T &res){
char c;T flag=1;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
}
const int N = 1e4 + 10;
const int M = 2e5 + 10;
int n,m;
int h[N],e[M],ne[M],idx;
int s,t;
bool check[N];
int dis[N];
queue<int > q;
int a[M * 2];

void add(int a,int b){
    e[idx] = b;ne[idx] = h[a];h[a] = idx++;
}

int main(){
    read(n);read(m);
    memset(h, -1, sizeof h);
    memset(dis,-1,sizeof dis);
    for(int i = 0;i < m * 2;i ++)  read(a[i]);
    for(int i = 0;i < m;i ++)  add(a[i * 2 + 1],a[i * 2]);
    read(s);read(t);
    q.push(t);
    check[t] = true;
    while(!q.empty()){
        int t = q.front();
        q.pop();
        for(int i = h[t];i != -1;i = ne[i]){
            int j = e[i];
            if(check[j] == true) continue;
			check[j] = true;
            q.push(j);
        }
    }
    memset(h, -1, sizeof h);
    memset(e,0,sizeof e);
    memset(ne,0,sizeof ne);
    idx = 0;
    for(int i = 0;i < m;i ++) add(a[i * 2],a[i * 2 + 1]);
    q.push(s);
    while(!q.empty()){
        int t = q.front();
        q.pop();
        for(int i = h[t];i != -1;i = ne[i]){
            int j = e[i];
            if(check[j] == false){
            	check[t] = false;
				continue;	
            } 
            q.push(j);
        }
    }
    q.push(s);
    dis[s] = 0;
    while(!q.empty()){
        int t = q.front();
        q.pop();
        for(int i = h[t];i != -1;i = ne[i]){
            int j = e[i];
            if(check[j] == true && dis[j] == -1){
                dis[j] = dis[t] + 1;
                q.push(j); 
            }
        }
    }
    if(dis[t] == -1) cout << "-1" << endl;
    else cout << dis[t] << endl;
    return 0;
}

AC代码

#include <bits/stdc++.h>
using namespace std;

#define IOS std::ios::sync_with_stdio(false);std::cin.tie(0);
const int N = 1e4 + 10, M = 2e5 + 10;
int n, m;
int h[N], e[M], ne[M], idx;
int h1[N], e1[M], ne1[M], idx1;
int state[N], dis[N]; // state[i]=true代表点i可以到达终点
int start, ed;

void add(int a, int b){
    e[idx] = b;ne[idx] = h[a];h[a] = idx++;
}

void add1(int a, int b){
    e1[idx1] = b;ne1[idx1] = h1[a];h1[a] = idx1++;
}

void bfs(int s){
    queue<int> q;
    q.push(s);
    memset(dis, -1, sizeof dis);
    dis[s] = 0;

    while (!q.empty()){
        int t = q.front();
        q.pop();
        state[t] = true;
        for (int i = h1[t]; i != -1; i = ne1[i]){
            int j = e1[i];
            if (dis[j] == -1){
                dis[j] = dis[t] + 1;
                q.push(j);
            }
        }
    }
}

int bfs1(int s){
    memset(dis, -1, sizeof dis);
    dis[s] = 0;
    queue<int> q;
    q.push(s);

    while (!q.empty()){
        int t = q.front();
        q.pop();

        for (int i = h[t]; i != -1; i = ne[i]){
            int j = e[i];
            if (dis[j] == -1){
                dis[j] = dis[t] + 1;
                bool flag = true;
                for (int k = h[j]; k != -1; k = ne[k]){
                    int kk = e[k];
                    if (state[kk] ==false){
                        flag = false;
                        break;
                    }
                }
                if (flag)
                    q.push(j);
                if (j == ed)
                    return dis[ed];
            }
        }
    }
    return -1;
}

int main(){
	IOS; 
    cin >> n >> m;
    int x, y;
    memset(h, -1, sizeof h);
    memset(h1, -1, sizeof h1);
    for (int i = 0; i < m; i++){
        cin >> x >> y;
        add(x, y);
        add1(y, x);
    }
    cin >> start >> ed;

    bfs(ed);
    cout << bfs1(start) << '\n';

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值