【NOIP2014】寻找道路(搜索)

题意:在有向无权图G中,找到从给定的S到T的最短路,满足【路径上所有点的出边指向的点都与终点连通】。

数据范围:n<=1e4, m<=2e5

思路:建图的时候正向和反向各存一张图,然后

1.在反向图中从终点DFS,路径上的所有点都是与终点连通的(标记为ok),但这些点不一定都是可行路径上的点。

2.枚举每一个不ok的点u,假若有ok的点v指向它(在反向图中,是u指向v),那那个ok的点v也不满足条件了。

3.这样标为ok的点就是可行路径上的点了,进行一次BFS就可以得到答案了。

在第2步中,我多建立了一个del数组,而不是直接在ok数组上修改,因为那样可能会产生迭代,让新的被删去的点可以再删掉新的点导致bug

具体见代码

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;

const int MAXN = 10050;

int n,m,s,t;
vector<int>v1[MAXN];
vector<int>v2[MAXN];//反向邻接表  dfs 
bool ok[MAXN];
bool del[MAXN];//第二次删点 
bool vis[MAXN];
int d[MAXN];

void dfs(int x)
{
    ok[x]=1;
    for(int i=0;i<v2[x].size();i++)
        if(!ok[v2[x][i]])dfs(v2[x][i]);
}

void bfs(int x)
{
    queue<int>q;
    d[x]=0;
    vis[x]=1;
    q.push(x);
    while(!q.empty())
    {
        int now=q.front();
        q.pop();
        for(int i=0;i<v1[now].size();i++)
        {
            int nx=v1[now][i];
            if(ok[nx]&&!vis[nx])
            {
                d[nx]=d[now]+1;
                vis[nx]=1;
                q.push(nx);
            }
        }
    }
}

int main()
{
    int t1,t2;
    scanf("%d%d",&n,&m);
    memset(d,0x3f,sizeof(d));
    for(int i=0;i<m;i++)
    {
        scanf("%d%d",&t1,&t2);
        if(t1==t2)continue;
        v1[t1].push_back(t2);
        v2[t2].push_back(t1);
    }
    //for(int i=1;i<=n;i++)ok[i]=1;
    scanf("%d%d",&s,&t);
    dfs(t);
    //重要:多一个数组,避免后效性(删点的过程可能会迭代)
    for(int i=1;i<=n;i++)
    {
        if(!ok[i])
        {
            for(int j=0;j<v2[i].size();j++)
            {
                if(ok[v2[i][j]]&&v2[i][j]!=s)del[v2[i][j]]=1;
            }
        }
    } 
    for(int i=1;i<=n;i++)if(del[i])ok[i]=0;
    bfs(s);
    if(d[t]==0x3f3f3f3f)printf("-1\n");
    else printf("%d\n",d[t]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Frenix/p/9206121.html