• 【极值问题】【CF1063B】 Labyrinth


    传送门

    Description

    给你一个(n~ imes~m)的矩阵,一开始你在第(r)行第(c)列。你的上下移动不受限制,向左最多移动(x)次,向右最多移动(y)次。求你最多能到多少个点。包括起始点。

    Input

    第一行是(n)(m),代表矩阵规模。

    第二行是(r)(c),代表你的位置

    第三行是(x)(y),代表移动限制

    下面(n)行每行(m)个字符,有且仅有'.'和''两种。如果第(i)行第(j)列是''代表你不能经过这个点。

    Output

    输出一行一个数代表能到的最多点数

    Sample Input

    4 5
    3 2
    1 2
    .....
    .***.
    ...**
    *....
    

    Sample Output

    10
    

    Hint

    (For~All:)

    (0~leq~n,m,r,c~leq~2000),(0~leq~x,y~leq~10^9)

    Solution

    朴素的(bfs)显然是对的,可以状态太多存不下。

    考虑如果从((sx,sy))点到一个点((x,y))时,假设共向右走了(r)步,向左走了(l)步,显然(r-l)是一个定值。具体的,(r-l~=~y-sy)。于是,对于任意一个目标((x,y)),发现(l)事实上与(r)线性正相关。对于一个点,显然到该点的(r)越小越好,同时由于(l)(r)线性正相关,所以最小化(r)的同时,(l)已经被最小化了。于是可以直接建图跑最短路,所有向右的边权为1,其他边权为0。跑完后扫描整个地图就可以判断合法性了。

    这里的一个新姿势是(0/1bfs)。当边权只有(0/1)时,可以使用双端队列进行bfs,具体的,当当前边的权值时(0)时,将终点压入队首,否则压入队尾。考虑这么做的正确性:易证任意一时刻队列中的点dist差值不超过1。于是正确性显然。(0/1bfs)的复杂度为(O(V+E))。相比dij少了一个log。

    Code

    #include<queue>
    #include<cstdio>
    #include<cstring>
    #define rg register
    #define ci const int
    #define cl const long long int
    
    typedef long long int ll;
    
    namespace IO{
        char buf[110];
    }
    
    template <typename T>
    inline void qr(T &x) {
        rg char ch=getchar(),lst=' ';
        while((ch > '9') || (ch < '0')) lst=ch,ch=getchar();
        while((ch >= '0') && (ch <= '9')) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
        if(lst == '-') x=-x;
    }
    
    template <typename T>
    inline void qw(T x,const char aft,const bool pt) {
        if(x < 0) {putchar('-');x=-x;}
        rg int top=0;
        do {
            IO::buf[++top]=x%10+'0';
        } while(x/=10);
        while(top) putchar(IO::buf[top--]);
        if(pt) putchar(aft);
    }
    
    template <typename T>
    inline T mmax(const T a,const T b) {return a > b ? a : b;}
    template <typename T>
    inline T mmin(const T a,const T b) {return a < b ? a : b;}
    template <typename T>
    inline T mabs(const T a) {return a < 0 ? -a : a;}
    
    template <typename T>
    inline void mswap(T &a,T &b) {
        T _temp=a;a=b;b=_temp;
    }
    
    const int maxn = 2010;
    
    const int fx[]={0,-1,0,1};
    const int fy[]={1,0,-1,0};
    const int fv[]={1,0,0,0};
    
    int n,m,sx,sy,x,y;
    int MU[maxn][maxn];
    char mp[maxn][maxn];
    
    std::deque<int>Qx,Qy;
    
    int main() {
        qr(n);qr(m);qr(sx);qr(sy);qr(x);qr(y);
        for(rg int i=1;i<=n;++i) scanf("%s",mp[i]+1);
        memset(MU,0x3f,sizeof MU);
        MU[sx][sy]=0; Qx.push_front(sx);Qy.push_front(sy);
        while(!Qx.empty()) {
            int hx=Qx.front(),hy=Qy.front();Qx.pop_front();Qy.pop_front();
            for(rg int i=0;i<4;++i) {
                int dx=hx+fx[i],dy=hy+fy[i];
                if((dx > n) || (dy > m) || (!dx) || (!dy) || (mp[dx][dy] == '*') || (MU[dx][dy] <= MU[hx][hy]+fv[i])) continue;
                MU[dx][dy]=MU[hx][hy]+fv[i];
                if(i) {Qx.push_front(dx);Qy.push_front(dy);}
                else {Qx.push_back(dx);Qy.push_back(dy);}
            }
        }
        rg int _ans=0;
        for(rg int i=1;i<=n;++i) {
            for(rg int j=1;j<=m;++j) if(mp[i][j] != '*') {
                if((MU[i][j] <= y) && ((MU[i][j]-j+sy) <= x)) ++_ans;
            }
        }
        qw(_ans,'
    ',true);
        return 0;
    }
    

    Summary

    当题设需要最小化多个变量时,不妨考虑变量间的相关关系,从此转化成单变量的极值问题。

    当边权只有(0)(1)的时候,可以考虑使用(0/1bfs),省去dij的log。

  • 相关阅读:
    201871010107-公海瑜《面向对象程序设计(java)》课程学习总结
    201871010107-公海瑜《面向对象程序设计(java)》第十七周学习总结
    201871010107-公海瑜《面向对象程序设计(java)》第十七周学习总结
    201871010107-公海瑜《面向对象程序设计(java)》第十六周学习总结
    201871010107-公海瑜《面向对象程序设计(java)》第十五周学习总结
    201871010107-公海瑜《面向对象程序设计(java)》第十四周学习总结
    《2019面向对象程序设计(java)课程学习进度条》
    201871010108-高文利 实验二 个人项目— {0-1}背包问题 项目报告
    201871010108-高文利《面向对象程序设计(java)》课程学习总结
    201871010108-高文利《面向对象程序设计(java)》第十七周学习总结
  • 原文地址:https://www.cnblogs.com/yifusuyi/p/9800308.html
Copyright © 2020-2023  润新知