• NOIP2014 寻找道路


    2.寻找道路

    (road.cpp/c/pas)

    【问题述】

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

    1.路径上的所有点的出边所指向的点都直接或间接与终点连通。

    2.在满足条件1的情况下使路径最短。

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

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

    【输入】

    输入文件名为road.in。

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

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

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

    【输出】

    输出文件名为road.out。

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

    【输入输出样例1】

    road.in

    road.out

    3 2

    1 2

    2 1

    1 3

    -1

    【输入输出样例说明】

     

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

    【输入输出样例2】

    road.in

    road.out

    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。

    【思路】

      很简单可以想到:先一遍搜索判断出满足条件的点,再一遍BFS计算最短路。

      学了别人的一种方法:对于结点的边建立一个链表,e[]记录边,first[]是表头,Edge 中的next域为指向下一条边的指针。很实用的方法。

      另外用到了一个技巧,正反向边依顺序加入,根据标号判断是正向还是反向,只用一个BFS完成操作。

      教训:提前就应该分析数据考虑怎样存储,不能想当然。

    【代码】

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<queue> 
    #include<vector>
    #define REP(a,b,c) for(int a=(b);a<=(c);a++)
    #define FOR(a,b,c) for(int a=(b);a<(c);a++)
    //注意如果REP(i,0,G[u].size()-1)会出BUG 
    //vector G[u]表示出边 不能用 n<=200000如果一个出边特别大的话就会超内存 
    //利用边的加入顺序达到记录反向边的效果 
    using namespace std;
    
    const int maxn = 600000;
    
    struct Edge{ 
      int u,v,next; 
    }edges[maxn]; 
    int en=0;
    int first[maxn];
    //存图方法: edges存正反向边 edges.size()==en firts[u]是邻接表的首
    //邻接表中存储相连边 
    
    int d[maxn];
    int vis[maxn]; 
    int n,m,s,t;
    
    void add(int u,int v){
        en++;
        edges[en].u=u; edges[en].v=v;
        edges[en].next=first[u];
        first[u]=en;
    }
    
    void bfs(int s,int flag) { //flag标志正反向 
        memset(d,-1,sizeof(d)); 
        queue<int> q; q.push(s);d[s]=0; 
        //vis[s]不要标记否则第一次把t标记正向不能访问 
        while(!q.empty()) {
            int u=q.front(); q.pop(); 
              for(int j=first[u];j>0;j=edges[j].next) if(j%2==flag && !vis[edges[j].v]) {
                  int v=edges[j].v;
                  if(d[v] == -1){  //d==-1 的判断  
                     d[v]=d[u]+1;
                     q.push(v);
                  }
              }
        }
        return ;
    }
    
    int main() {
        memset(first,-1,sizeof(first));
        scanf("%d%d",&n,&m);
        REP(i,1,m) { 
            int u,v; scanf("%d%d",&u,&v);
            add(u,v);  //正反向添加两条边 关系为相 ^ 
            add(v,u);
        }
        scanf("%d%d",&s,&t);
        
        bfs(t,0);  //%==0为反向边 
        for(int j=1;j<=2*m;j+=2) if(d[edges[j].v]==-1)  //由can推vis 而不能直接在can上操作 
        //注意只枚举正向边 
          vis[edges[j].v]=vis[edges[j].u]=1;
           
        bfs(s,1);
        printf("%d",d[t]);
        return 0; 
    }
  • 相关阅读:
    php数组转json 中文编码错乱解决
    fastadmin展示多个视频按钮
    fastadmin在json字段动态关联
    fastadmin放大镜改为“搜索”
    fastadmin行内手机验证规则
    fastadmin批量修改状态
    php手机、邮箱规则验证
    tp5 查询某个值是否在数据表某一个字段中 FIND_IN_SET
    css书写规范以及如何写出赏心悦目的代码
    html5中常见选择器
  • 原文地址:https://www.cnblogs.com/lidaxin/p/4814688.html
Copyright © 2020-2023  润新知