[NOIP2014真题]寻找道路

来源:互联网 发布:origin软件官方下载 编辑:程序博客网 时间:2024/05/18 22:46

先给一个数据不水的提交址:http://uoj.ac/problem/19

题目背景
NOIP2014 提高组 Day2 试题。

题目描述
在有向图 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
样例数据2
输入
6 6
1 2
1 3
2 6
2 5
4 5
3 4
1 5
输出
3

备注
【样例1说明】
这里写图片描述
如上图所示,箭头表示有向道路,圆点表示城市。起点 1 与终点 3 不连通,所以满足题目描述的路径不存在,故输出 -1。

【样例2说明】
这里写图片描述
如上图所示,满足条件的路径为 1->3->4->5。注意点 2 不能在答案路径中,因为点 2 连了一条边到点 6,而点 6 不与终点 5 连通。

【数据说明】
对于 30% 的数据,0<n100<m20
对于 60% 的数据,0<n1000<m2000
对于 100% 的数据,0<n10,0000<m200,0000<xystnxt

分析: 这道题正着十分难做,考虑反着做。反向建图,从终点往起点走,走不到的地方便是正着走不直接或间接指向终点的点。给他们打上标记,并把与他们距离为1的点也打上标记。之后相当于删掉这些点和指向他们的边,构造新图走dijkstra(SPFA也可)。注意,要特判起点,因为在我的算法中是删掉指向不满足点的边,但是起点没有入边,所以被UOJhack数据卡了。

代码:

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<string>#include<ctime>#include<cmath>#include<algorithm>#include<cctype>#include<iomanip>#include<queue>#include<set>using namespace std;int getint(){    int f=1,sum=0;    char ch;    for(ch=getchar();(ch>'9'||ch<'0')&&ch!='-';ch=getchar());    if(ch=='-')    {        f=-1;        ch=getchar();    }    for(;ch>='0'&&ch<='9';ch=getchar())        sum=(sum<<3)+(sum<<1)+ch-48;    return sum*f;}int tot,x,y,n,m,s,t,dis[10010];bool bj1[10010],bj2[10010],visit[10010],exist[200010];int first1[10010],nxt1[200010],to1[200010],w[200010],first2[10010],nxt2[200010],to2[200010];queue<int> que; void addedge(int x,int y)//正反同时建边{    tot++;    nxt1[tot]=first1[x];    first1[x]=tot;    to1[tot]=y;    w[tot]=1;    nxt2[tot]=first2[y];    first2[y]=tot;    to2[tot]=x;}void dfs(int u){    bj1[u]=0;    for(int p=first2[u];p;p=nxt2[p])    {        int v=to2[p];        if(!visit[v])        {            visit[v]=1;            dfs(v);        }    }}void SPFA(){    dis[s]=0;    que.push(s);    visit[s]=1;    while(!que.empty())    {        int u=que.front();        que.pop();        visit[u]=0;        for(int p=first1[u];p;p=nxt1[p])        {            if(exist[p])            {                int v=to1[p];                if(dis[v]>dis[u]+w[p])                {                    dis[v]=dis[u]+w[p];                    if(!visit[v])                    {                        visit[v]=1;                        que.push(v);                    }                }            }        }    }}int main(){    freopen("road.in","r",stdin);    freopen("road.out","w",stdout);    n=getint();m=getint();    for(int i=1;i<=m;++i)    {        x=getint();y=getint();        addedge(x,y);    }    s=getint();t=getint();    memset(bj1,1,sizeof(bj1));//把所有点都打标记,然后dfs删掉能走到的点的标记    dfs(t);    for(int i=1;i<=n;++i)//与有标记的点距离为1的点也打上标记        if(bj1[i])            for(int p=first2[i];p;p=nxt2[p])            {                int v=to2[p];                bj2[v]=1;//为什么要用新标记呢?因为用bj1会导致把与这些新标记的点距离为1的点也打上标记,如此恶性循环,整个图全是标记了。            }    memset(exist,1,sizeof(exist));    for(int i=1;i<=tot;++i)//删边    {        int v=to1[i];        if(bj1[v]==1||bj2[v]==1)            exist[i]=0;    }    if(bj1[s]==1||bj2[s]==1)//特判起点    {        cout<<-1<<endl;        return 0;    }    memset(dis,127,sizeof(dis));    memset(visit,0,sizeof(visit));    SPFA();    if(dis[t]>200000)//终点没更新,就是走不通        cout<<-1<<endl;    else        cout<<dis[t]<<endl;    return 0;}

本题结。

原创粉丝点击