zoukankan      html  css  js  c++  java
  • [HAOI2007]修筑绿化带 题解

    题意分析

    给出一个 $m*n$ 的矩阵 $A$ ,要求从中选出一个 $a*b$ 的矩阵 $B$ ,再从矩阵 $B$ 中选出一个 $c*d$ 的矩阵 $C$ ,要求矩阵 $B,C$ 的边界不能重合,求矩阵 $C$ 在矩阵 $B$ 中的补集的权值和的最大值。

    思路分析

    通过分析题目后可以发现本题可以用二维单调队列进行求解,同时利用到了二维前缀和。即先对其中一维进行求解,然后再求解另一维。

    具体实现

    1. 预处理二维前缀和

    设 $suml_{i,j}$ 表示以 $(i,j)$ 为右下角的矩形 $B$ 的权值和, $sums_{i,j}$ 表示以 $(i,j)$ 为右下角的矩形 $C$ 的权值和,$sum_{i,j}$ 表示 $(i,j)$ 的二维前缀和。递推式显然,就不赘述了。

    2. 利用单调队列求解列的答案

    设 $f1_{i,j}$ 表示以 $(i,j-b+d+1sim j-1)$ 为右下角的矩阵 $C$ 中权值和的最大的矩阵,则对于每个 $j$ ,有:

    • 从队头排除过时决策
    • 若能构成矩阵 $B$ ,即 $jgeq b$,则队头为当前答案
    • 从队尾排除无用决策
    • 从队尾插入当前决策

    3. 利用单调队列求解列的答案

    设 $f2_{i,j}$ 表示以 $(i-a+c+1sim i,j-b+d+1sim j-1)$ 为右下角的矩阵 $C$ 中权值和的最大的矩阵,求解过程与上步类似,就不赘述了。

    4. 遍历一遍找出答案

    此时 $suml_{i,j}-f2_{i,j}$ 就是以 $(i,j)$ 为右下角的矩阵 $B$ 的答案,遍历所有矩阵 $B$ ,找出最大值即可。

    有一个需要注意的点,矩阵 $B,C$ 的边界不能重合,在递推的时候下标需要注意。举例:$(c,d)$ 不能作为决策和答案,$(c+1,d+1)$ 只能作为决策而不能作为答案。

    #include<iostream>
    #include<cstdio>
    #include<queue>
    using namespace std;
    const int N=1100;
    int m,n,a,b,c,d,ans;
    int s[N][N],sum[N][N],suml[N][N],sums[N][N],f1[N][N],f2[N][N];
    deque<int> q;
    int main()
    {
        scanf("%d%d%d%d%d%d",&m,&n,&a,&b,&c,&d);
        for(int i=1;i<=m;i++)
            for(int j=1;j<=n;j++)
                scanf("%d",&s[i][j]),sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+s[i][j];//递推二维前缀和
        for(int i=a;i<=m;i++)
            for(int j=b;j<=n;j++)
                suml[i][j]=sum[i][j]-sum[i-a][j]-sum[i][j-b]+sum[i-a][j-b];
        for(int i=c+1;i<=m;i++)
            for(int j=d+1;j<=n;j++)
                sums[i][j]=sum[i][j]-sum[i-c][j]-sum[i][j-d]+sum[i-c][j-d];//预处理
        for(int i=c+1;i<=m;i++)
        {
            q.clear();q.push_back(d+1);
            for(int j=d+2;j<=n;j++)
            {
                while(q.size() && q.front()<j-b+d+1)
                    q.pop_front();
                if(j>=b)
                    f1[i][j]=sums[i][q.front()];
                while(q.size() && sums[i][q.back()]>=sums[i][j])
                    q.pop_back();
                q.push_back(j);
            }
        }//求解第一维
        for(int j=b;j<=n;j++)
        {
            q.clear();q.push_back(c+1);
            for(int i=c+2;i<=m;i++)
            {
                while(q.size() && q.front()<i-a+c+1)
                    q.pop_front();
                if(i>=a)
                    f2[i][j]=f1[q.front()][j];
                while(q.size() && f1[q.back()][j]>=f1[i][j])
                    q.pop_back();
                q.push_back(i);
            }
        }//求解第二维
        for(int i=a;i<=m;i++)
            for(int j=b;j<=n;j++)
                ans=max(ans,suml[i][j]-f2[i][j]); //找出答案
        printf("%d",ans);
        return 0;
    }
  • 相关阅读:
    记一次与用户的亲密接触
    拨开障目的叶,一览CMDB庐山真面目
    广通软件获“2016年度中国最具影响力IT运维管理软件提供商”殊荣
    CMDB三大绝招,助我站稳运维之巅
    datetime module总结
    Python time module总结
    IPMItool小结
    Python selenium 延时的几种方法
    Python 字典操作
    YUM 配置
  • 原文地址:https://www.cnblogs.com/TEoS/p/13460862.html
Copyright © 2011-2022 走看看