[2019.3.8]BZOJ2241 [SDOI2011]打地鼠

如果我们确定了(r)(c),事实上也确定了打地鼠的方案。

因为所有(r imes c)的矩形中,只有1个能覆盖当前还有地鼠的位置组成的图形的角落上的那个洞,所以我们只能在那个角落上砸。

比如,我们目前剩下的地鼠分布为

0 0 0 0

0 0 1 2

0 2 1 2

2 2 3 1

那么我们只能以第二行的第三个位置(那个加粗的1)为左上角砸1次。

做法1

于是我们可以(O(nm))枚举(r),枚举(c),然后(O(nm))从左到右,从上到下枚举点,然后(O(rc))给以当前点为左上角的矩形内的数减去当前点剩下的地鼠数量,寻找(r imes c)的最大值。

然而这样做是(O(n^3m^3))的。

我们可以做一下二维差分,然后省去了最后给矩形内点减去当前点剩下的地鼠数量的步骤。

二维差分:给一个左上端点为((x_1,y_1)),右下端点为((x_2,y_2))的矩形加上(v),需要在差分数组的(s_{x_1,y_1})(s_{x_2+1,y_2+1})加上(v),给(s_{x_1,y_2+1})(s_{x_2+1,y_1})减去(v),那么左上端点为((1,1)),右下端点为((x,y))的矩形的和就是点((x,y))上的实际值了。

时间复杂度(O(n^2m^2))

code:

#include<bits/stdc++.h>
#define Add(u,l,d,r,v) (sum[u][l]+=v,sum[d+1][r+1]+=v,sum[u][r+1]-=v,sum[d+1][l]-=v)
using namespace std;
int n,m,num[110][110],sum[110][110],ans,t,SUM;
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)scanf("%d",&num[i][j]),SUM+=num[i][j];
    for(int r=1;r<=n;++r){
        for(int c=1;c<=m;++c){
            for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)sum[i][j]=0;
            for(int i=1;i<=n;++i){
                for(int j=1;j<=m;++j){
                    sum[i][j]+=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
                    if(sum[i][j]>num[i][j])goto END;
                    if(sum[i][j]<num[i][j]){
                        if(i+r-1>n||j+c-1>m)goto END;
                        t=num[i][j]-sum[i][j],Add(i,j,i+r-1,j+c-1,t);
                    }
                }
            }
            ans=max(ans,r*c);
            END:
            ;
        }
    }
    printf("%d",SUM/ans);
    return 0;
}

做法2

我们枚举(r),假定(c=1),计算最大的合法的(r);

然后假定(r=1),枚举(c),计算最大的合法的(c);

那么(r imes c)锤子一定是可行的。

为什么呢?

证明:

我们每次锤的点是目前地鼠数非0的位置组成的图形中最上一行最左一列的点,我们设这个点还有(x)只地鼠。

首先,以这个点为左上端点,锤一个(1 imes c)的矩形(x)次是合法的;

其次,以这一行前(c)个点每一个为左上端点,各锤一个(r imes 1)的矩形(x)次是合法的。

所以这个(r imes c)矩形也就是合法的了。

时间复杂度(O((n+m)nm))

code:

#include<bits/stdc++.h>
using namespace std;
int n,m,num[110][110],sum[110][110],r,c,t,SUM;
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)scanf("%d",&num[i][j]),SUM+=num[i][j];
    for(r=n;r>=1;--r){
        for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)sum[i][j]=0;
        for(int i=1;i<=n;++i)for(int j=1;j<=m;++j){
            sum[i][j]+=sum[i-1][j];
            if(sum[i][j]>num[i][j])goto END1;
            if(sum[i][j]<num[i][j]){
                if(i+r-1>n)goto END1;
                else t=num[i][j]-sum[i][j],sum[i][j]+=t,sum[i+r][j]-=t;
            }
        }
        break;
        END1:
        ;
    }
    for(c=n;c>=1;--c){
        for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)sum[i][j]=0;
        for(int i=1;i<=n;++i)for(int j=1;j<=m;++j){
            sum[i][j]+=sum[i][j-1];
            if(sum[i][j]>num[i][j])goto END2;
            if(sum[i][j]<num[i][j]){
                if(j+c-1>m)goto END2;
                else t=num[i][j]-sum[i][j],sum[i][j]+=t,sum[i][j+c]-=t;
            }
        }
        break;
        END2:
        ;
    }
    printf("%d",SUM/(r*c));
    return 0;
}
原文地址:https://www.cnblogs.com/xryjr233/p/BZOJ2241.html