zoukankan      html  css  js  c++  java
  • Walk

    在比特镇一共有 n 个街区,编号依次为 1 到 n,它们之间通过若干条单向道路连接
    比特镇的交通系统极具特色,除了 m 条单向道路之外,每个街区还有一个编码 vali,不同街区可能拥有相同的编码。如果 vali and valj = valj,即 vali 在二进制下与 valj 做与运算等于 valj,那么也会存在一条额外的从 i 出发到 j 的单向道路
    Byteasar 现在位于 1 号街区,他想知道通过这些道路到达每一个街区最少需要多少时
    因为比特镇的交通十分发达,你可以认为通过每条道路都只需要 1 单位时间

    Input
    第一行包含两个正整数 n;m,表示街区的总数以及道路的总数
    第二行包含 n 个正整数 val1; val2; ……; valn,分别表示每个街区的编码
    接下来 m 行,每行包含两个正整数 ui; vi,表示一条单向道路,起点为 ui,终点为 vi
    Output
    输出 n 行,每行一个整数,其中第 i 行输出到达第 i 个街区的最少时间,如果无法到达则输出 -1

    Examples
    walk.in
    5 2
    5 4 2 3 7
    1 4
    2 3

    walk.out
    0
    1
    2
    1
    -1

    分析:
    考场上dij,稳妥40

    看一下官方题解:
    依旧考虑新增 2^20个点
    i只需要向i去掉某一位的1的点连边
    这样一来图的边数就被压缩到了 20*2^20+2n+m,然后
    BFS求出1到每个点的最短路即可
    时间复杂度 O(20*2^20+n+m)

    第一眼看到这个,我的内心:mmp,ta在说什么。。。

    解释一下:
    特殊边的处理是非常恶心的,因为我们不能枚举判定,也不能直接相连(n^2),所以需要考虑转化
    新增 2^20个点:不知道为什么是这么多。。。
    i只需要向i去掉某一位的1的点连边,边权是0:
    假设是第11个点,
    11的二进制是1011,
    也就是说,11号点会向1010(10),0011(3),1001(9)三个点连线
    这里写图片描述
    我们可以通过走多条这种有向边的方式到达他的所有子集点,即i&j=j的点。
    对于原来的n个点,先把m条边连好,连权值为1的有向边,
    然后对于i号点,由它向新增的第val[i]个点(就是他权值所对应的点)连一条权值为1的有向边
    再由新增的第val[i]个点向它连一条权值为0的有向边。
    BFS 的时候,每次要把用0权值的边连接的所有点都加入队尾,以保证距离不降

    这里写代码片
    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<queue>
    
    using namespace std;
    
    const int INF=0x33333333;
    const int N=300010;
    int n,m,cnt;
    struct node{
        int x,y,v,nxt;
    };
    node way[N<<2];
    int st[N+(1<<20)],tot=0,d[N+(1<<20)];
    queue<int> q;
    
    void add(int u,int w,int z)
    {
        tot++;
        way[tot].x=u;way[tot].y=w;way[tot].v=z;
        way[tot].nxt=st[u];st[u]=tot;
    }
    
    //把所有与x相连的边权为0的点加入队列 
    void dfs(int x,int len)  //len是路径长度 
    {
        if (d[x]>=0) return;  //已经遍历过了 
        q.push(x);
        d[x]=len;
        for (int i=st[x];i;i=way[i].nxt)
            if (way[i].v==0)  //费用是0 
               dfs(way[i].y,len);
        if (x>=cnt) return;
        for (int i=0;i<20;i++)  
            if (x>>i&1)  //二进制第i位上是1 
               dfs(x^(1<<i),len);  //按位异或,相同是0,不同是1 
    //其实这一部分的dfs就相当于从val向与val差1的数连边,节省了连边的时间和空间 
    }   
    
    int main()
    {
        //freopen("walk.in","r",stdin);  
        //freopen("walk.out","w",stdout);
        scanf("%d%d",&n,&m);
        cnt=1<<20;  //2^20个增加的点 
        for (int i=1;i<=n;i++)   
        {
            int x;
            scanf("%d",&x);
            add(i+cnt,x,1);  //+cnt把前面需要增加的点的位置空出来 
            add(x,i+cnt,0);
        }
        for (int i=1;i<=m;i++)
        {
            int u,w;
            scanf("%d%d",&u,&w);
            add(u+cnt,w+cnt,1);
        }
        for (int i=0;i<=cnt+n;i++) d[i]=-1;
        dfs(cnt+1,0);
        while (!q.empty())  //bfs 
        {
            int now=q.front();
            q.pop();
            for (int i=st[now];i;i=way[i].nxt)
                if (way[i].v==1)  //距离++ 
                   dfs(way[i].y,d[now]+1);
        }
        for (int i=1;i<=n;i++)
            printf("%d
    ",d[i+cnt]);
        return 0;
    }
  • 相关阅读:
    Python 学习笔记 -- 列表的基本使用
    帮助你构建云服务的开源平台:openstack
    一个基于Asterisk构建的VOIP应用软件:Elastix介绍
    Asterisk项目概述
    Asterisk 代码架构概述
    云计算投入成本分析
    撕去服务器虚拟化和云计算的神秘面纱
    VOIP开源项目源码地址
    websocket技术
    JSP页面静态化总结之一使用URLRewrite实现url地址伪静态化
  • 原文地址:https://www.cnblogs.com/wutongtong3117/p/7673532.html
Copyright © 2011-2022 走看看