Painting the Barn 2 (最大子矩阵)

测试链接

题面:

Farmer John is not good at multitasking. He gets distracted often, making it hard to complete long projects. Currently, he is trying to paint one side of his barn, but he keeps painting small rectangular areas and then getting sidetracked by the needs of tending to his cows, leaving some parts of the barn painted with more coats of paint than others.

We can describe the side of the barn as a 2D x–y plane, on which Farmer John paints N rectangles, each with sides parallel to the coordinate axes, each described by the coordinates of its lower-left and upper-right corner points.

Farmer John wants to apply several coats of paint to the barn so it doesn’t need to be repainted again in the immediate future. However, he doesn’t want to waste time applying an excessive number of coats of paint. It turns out that K coats of paint is the optimal amount. However, looking at the amount of area covered by K coats of paint, he is not very happy. He is willing to paint up to two additional rectangles to try and increase this area, as long as these two rectangles are disjoint (not sharing any positive amount of area in common). Note that he can also decide to paint zero new rectangles or just one new rectangle if this ends up being the best thing to do.

INPUT
The first line of input contains N and K (1≤K,N≤1e5). Each of the remaining N lines contains four integers x1,y1,x2,y2 describing a rectangular region being painted, with lower-left corner (x1,y1) and upper-right corner (x2,y2) . All x and y values are in the range 0…200 , and all rectangles have positive area.

Like the rectangles he already painted, any new rectangles that Farmer John paints must have positive area, and their corner points must have x and y coordinates in the range 0…200.

OUTPUT
Please output the maximum area of the barn that could be covered by exactly K coats of paint, if Farmer John paints up to two additional disjoint rectangles.

SAMPLE INPUT:
3 2
1 1 4 4
3 3 7 6
2 2 8 7
SAMPLE OUTPUT:
26


题意:

有一个200*200的矩形区域,在里面画n个矩形,最后可以额外任意画零个、一个或两个矩形(两个矩形时要保证两个矩形不相交),询问最后整个区域内正好被k个矩形覆盖的面积。

思路:

这题的简化版是最后没有额外矩阵,那么就只需要对每个要画的矩阵打一个二维差分的标记,然后用二维前缀和判断每个小块的值是否等于k,把等于k的块数统计下来就是答案。

这道题先和简化版的一样,先求出在不加额外矩阵的情况下的等于k的块数,然后把原来的图变一下,把等于k-1的变为1,把等于k的变为-1,其余的都为0。因为覆盖一个矩阵后,原来k-1的块就会变为k,会增加答案,而原来就是k的会变为k+1,就会减少答案,而其他的都不会都答案造成影响。现在的图中只有0,1,-1,求怎么样覆盖矩阵可以使答案最大就变成了在现在的图中如何选择举证可以使被选中区域的和最大。

先考虑一个简单问题,在一个一维的数组中求得最大的子区间和,显然可以dp[i]=max(dp[i-1]+num[i],num[i])这样扫一遍就可以了,复杂度只需要On。

如果是在二维中求最大子矩阵的和呢?可以先On^2枚举子矩阵的上边界和下边界,然后对于每一列,把上边界和下边界内的数值求和,这样二维就被压成了一维,然后用刚才的方法求出最大的子区间和,这个过程是在确定矩阵的左边界和右边界。当然求出的值还需要跟0取个max,因为可以不画额外的矩阵。因为可以画两个矩阵,观察两个矩阵的分布,发现只有两种情况,1.以一条竖线为界,一个在左,另一个在右。2.以一条水平线为界,一个在上,另一个在下。刚才把二维压缩成一维后需要做两次,一次从左往右,另一次从右往左。这里处理的是左右分布的情况,上下分布的相似的也需要处理。

最后原来求出的答案再加上子矩阵最大和便是答案。

复杂度On^3


参考代码:

#include
using namespace std;

int mp[205][205];//原图
int nmp[205][205];//新图
int sum1[205][205];//竖着的前缀和
int sum2[205][205];//横着的前缀和
int dp[205];//临时的dp数组
int li[205],ri[205],up[205],dw[205];//分别记录从最左到位置x的最大子矩阵和,最右...,最上...,最下

int main(){
    int n,k;
    scanf("%d%d",&n,&k);
    int x1,y1,x2,y2;
    for(int i=1;i<=n;i++){
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        x1++,y1++,x2++,y2++;//坐标整体加1,为了前缀和写的方便一点
        mp[x1][y1]++;mp[x1][y2]--;
        mp[x2][y1]--;mp[x2][y2]++;
    }
    int cnt=0;
    for(int i=1;i<=200;i++){
        for(int j=1;j<=200;j++){
            mp[i][j]+=mp[i][j-1]+mp[i-1][j]-mp[i-1][j-1];
            if(mp[i][j]==k){
                nmp[i][j]=-1;
                cnt++;//统计原来答案
            }
            else if(mp[i][j]==k-1){
                nmp[i][j]=1;
            }
        }
    }
    //printf("%d\n",cnt);
    //分别作竖着的和横着的前缀和
    for(int j=1;j<=200;j++){
        for(int i=1;i<=200;i++){
            sum1[i][j]=sum1[i-1][j]+nmp[i][j];
        }
    }

    for(int i=1;i<=200;i++){
        for(int j=1;j<=200;j++){
            sum2[i][j]=sum2[i][j-1]+nmp[i][j];
        }
    }

    int ans=0;
    memset(dp,0,sizeof(dp));
    for(int i=1;i<=200;i++){
        for(int j=i;j<=200;j++){//前两层枚举上下边界
            for(int k=1;k<=200;k++){//枚举列坐标
                int tmp=sum1[j][k]-sum1[i-1][k];//利用前缀和压缩成一维
                dp[k]=max(0,max(tmp,dp[k-1]+tmp));
                li[k]=max(li[k],dp[k]);
            }
        }
    }
    memset(dp,0,sizeof(dp));
    for(int i=1;i<=200;i++){
        for(int j=i;j<=200;j++){
            for(int k=200;k>=1;k--){
                int tmp=sum1[j][k]-sum1[i-1][k];
                dp[k]=max(0,max(tmp,dp[k+1]+tmp));
                ri[k]=max(ri[k],dp[k]);
            }
        }
    }
    memset(dp,0,sizeof(dp));
    for(int i=1;i<=200;i++){
        for(int j=i;j<=200;j++){
            for(int k=1;k<=200;k++){
                int tmp=sum2[k][j]-sum2[k][i-1];
                dp[k]=max(0,max(tmp,dp[k-1]+tmp));
                up[k]=max(up[k],dp[k]);
            }
        }
    }
    memset(dp,0,sizeof(dp));
    for(int i=1;i<=200;i++){
        for(int j=i;j<=200;j++){
            for(int k=200;k>=1;k--){
                int tmp=sum2[k][j]-sum2[k][i-1];
                dp[k]=max(0,max(tmp,dp[k+1]+tmp));
                dw[k]=max(dw[k],dp[k]);
            }
        }
    }
	//作前缀最大值
    for(int i=1;i<=200;i++){
        li[i]=max(li[i],li[i-1]);
        up[i]=max(up[i],up[i-1]);
    }
    for(int i=200;i>=1;i--){
        ri[i]=max(ri[i],ri[i+1]);
        dw[i]=max(dw[i],dw[i+1]);
    }
    //枚举分界线统计答案
    for(int i=1;i<=200;i++){
        ans=max(ans,li[i]+ri[i+1]);
        ans=max(ans,up[i]+dw[i+1]);
    }
    //printf("%d\n",ans);
    printf("%d\n",cnt+ans);
    return 0;
}

你可能感兴趣的:(矩阵)