zoukankan      html  css  js  c++  java
  • 机房测试5:reverse(bfs+set 或 线段树优化建图)

    题目:

     

     分析:

    首先画样例分析一下,会发现如果要求一个位置要多少次翻转,就将这个位置向与它关联的点连边(关联点指的是可能与它值互换的位置),一直连到起点为止,连边的次数即为它所需步数。

    所以转换成求单源最短路,因为边权为1,可以用bfs。

    但是这道题n的范围很大,刚刚的做法是n*k的,考虑优化。

    法1:在建图上优化

    题目要求的是区间翻转,所以也对应着相关性质:每个点连边一定是都连的奇数点或偶数点(画图可知),且这些奇数偶数点都对应着一段连续的区间。

    如果可以将点向点连边优化成点向区间连边,复杂度就可以大大减小。

    怎么连呢?

    用两颗线段树维护奇数点集和偶数点集,每次连一段区间的时候转换成与线段树中区间对应的点连边,保证了每个点最多连log条边。

    然后再bfs即可。

    线段树优化建图模板

    法2:在bfs中优化

    我们其实可以不连边,直接从s点开始bfs,更新每一个第一次被遍历点的操作次数。

    每一次取出一个点,在遍历与其相关的点的时候,在x-k+1~x+k-1这个范围内找满足条件的数。

    找的时候花费了k的复杂度,但有些已经被更新过的点是没有必要再访问一次的

    怎么优化呢?(明显是不能vis打标记的,因为我们for的时候还是会访问到它)

    通过用set记录当前区间中未访问到的点有哪些,每次只for这些点即可。访问后在set中erase。

    优化后复杂度O(n*logn)(set删除元素,加入元素自带logn)

    注意代码细节:

    1.当x-k+1<1了,区间就不再是1~x+k-1了,要重新计算新的l’(注意不能把1当做l,因为本应该翻转的区间取不到1)

    2.set的erase只能这样打:se[...].erase(it++

    it++不能放在外面!!!(神奇的stl)

    #include<bits/stdc++.h>
    using namespace std;
    #define ri register int
    int dis[100005],vis[100005],pos[100005];
    int n,k,m,s,a;
    queue<int>q;
    set<int> se[2];
    set<int> :: iterator it;
    int read()
    {
        int x=0,fl=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') fl=-1; ch=getchar(); }
        while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=getchar();
        return fl*x;
    }
    int main()
    {
        freopen("reverse.in","r",stdin);
        freopen("reverse.out","w",stdout);
        n=read(); k=read(); m=read(); s=read();
        for(ri i=1;i<=m;++i) a=read(),pos[a]=1;
        for(ri i=1;i<=n;++i) if(!pos[i] && i!=s) se[i&1].insert(i);
        memset(dis,-1,sizeof(dis));
        q.push(s); dis[s]=0;
        while(!q.empty()){
            int u=q.front(); q.pop();
            int l=u-k+1,r=u+k-1,op=(k&1)^(u&1)^1;//判断连边的奇偶:如果同为奇数或同为偶数 就走奇数点 反则走偶数点
            //画线段推一下公式 处理边界超出后 它实际能够走到的点 
            if(l<1) l=k-u+1;
            if(r>n) r=2*n+1-u-k;
            it=se[op].lower_bound(l);
            while(it!=se[op].end() && *it<=r){
                dis[*it]=dis[u]+1;
                q.push(*it);
                se[op].erase(it++);//保证了每一个点只能走一次 
            }
        }
        for(int i=1;i<=n;++i) printf("%d ",dis[i]);
    }
    /*
    10 4 3 3
    2 5 10
    */
    View Code
  • 相关阅读:
    博客第8周
    剑指offer 位运算
    真题
    剑指offer分类刷题(转载)
    PV PVC StorageClass是什么?
    k8s使用rbac实现多租户
    ansible-playbook 使用blockinfile 修改/etc/hosts 主机名
    ansible-playbook 一键部署ntp时间同步 yml
    简单搭建一个HTTP文件下载服务器
    常用docker命令备忘
  • 原文地址:https://www.cnblogs.com/mowanying/p/11625496.html
Copyright © 2011-2022 走看看