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
  • 相关阅读:
    JDBC 复习4 批量执行SQL
    JDBC 复习3 存取Oracle大数据 clob blob
    Oracle复习
    Linux命令(1)grep
    JDBC 复习2 存取mysql 大数据
    JDBC 复习1 DBUtil
    php 环境搭建问题
    Windows 批处理 bat 开启 WiFi 菜单选项 设置ID PWD
    Bat 批处理启动和停止Oracle 服务
    docker 学习1 WSL docker ,Windows docker
  • 原文地址:https://www.cnblogs.com/mowanying/p/11625496.html
Copyright © 2011-2022 走看看