【花样枚举】bzoj 1177 apio2009采油区域

来源:互联网 发布:linux 文件系统介绍 编辑:程序博客网 时间:2024/04/28 18:38

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1177
题目大意:给你一个矩阵,让你选出三个边长为k且互不重叠的正方形使得所有正方形内的数的和最大

这题正解还真是枚举……不过这个枚举很厉害的说
最暴力就是枚举三个正方形的位置,不过一定不会AC……
但是多思考一下会发现一件事情:任意两条直线都可以把矩阵分为三部分,且可以做到将三个正方形分别分到三部分里
1.可以通过两条互相垂直的线分成三块,如下图

这里写图片描述 这里写图片描述

这里写图片描述 这里写图片描述

2.可以通过两条平行的平行的线分成三块,如下图

这里写图片描述 这里写图片描述

知道了这个可以做什么呢?反正正方形一定在被分出来的每一部分里对吧……所以我们可以通过维护每一部分的正方形的最大值来快速寻找每个部分里的正方形
枚举两条线,然后通过 二维前缀和+dp 或者 二维前缀和+萝比特酱(lowbit) 维护下最大值就可以了,dp的维护方法就是xx[i][j]代表以(i,j)为右下角,(1,1)为左上角的区域内的最大值,然后再开三个类似的数组……

贴代码(dp)

#include <cstdio>#include <cstring>#include <iostream>#define INF 2147483647#define MAXN 2000using namespace std;int a[MAXN][MAXN];int s[MAXN][MAXN];int xx[MAXN][MAXN];int xy[MAXN][MAXN];int yx[MAXN][MAXN];int yy[MAXN][MAXN];int lx[MAXN];int ly[MAXN];int n,m,k;int ans = 0;int p,q;int o;int read(){    int x = 0;    char w;    w = getchar();    while(w >= '0' && w <= '9')    {        x *= 10;        x += w - '0';        w = getchar();    }    return x;}int nico(int x,int y){    return s[x][y-1] + s[x-1][y] - s[x-1][y-1] + a[x][y];}int poi(int x,int y){    if(x < k || y < k)        return 0;    return s[x-k][y-k] - s[x][y-k] - s[x-k][y] + s[x][y];}void dp(){    for(int i = k;i <= o;i ++)    {        for(int j = k;j <= o;j ++)        {            xx[i][j] = max(xx[i-1][j],xx[i][j-1]);            xx[i][j] = max(xx[i][j],poi(i,j));        }    }    for(int i = k;i <= o;i ++)    {        for(int j = o;j >= k;j --)        {            xy[i][j] = max(xy[i-1][j],xy[i][j+1]);            xy[i][j] = max(xy[i][j],poi(i,j));        }    }    for(int i = o;i >= k;i --)    {        for(int j = k;j <= o;j ++)        {            yx[i][j] = max(yx[i+1][j],yx[i][j-1]);            yx[i][j] = max(yx[i][j],poi(i,j));        }    }    for(int i = o;i >= k;i --)    {        for(int j = o;j >= k;j --)        {            yy[i][j] = max(yy[i+1][j],yy[i][j+1]);            yy[i][j] = max(yy[i][j],poi(i,j));        }    }    for(int i = 1;i <= o;i ++)    {        for(int j = 1;j <= o;j ++)        {            lx[i] = max(lx[i],poi(i,j) );            ly[i] = max(ly[i],poi(j,i) );        }    }    return ;}void sol(){    int minn;    for(int i = k;i <= o;i ++)    {        for(int j = k;j <= o;j ++)        {            ans = max(ans,xx[i][j] + xy[i][j+k] + yx[i+k][m]);            ans = max(ans,xx[i][j] + yx[i+k][j] + xy[n][j+k]);            ans = max(ans,xy[i][j+k] + yy[i+k][j+k] + xx[n][j]);            ans = max(ans,yx[i+k][j] + yy[i+k][j+k] + xx[i][m]);        }    }    for(int i = k;i <= n;i ++)        for(int j = i+k;j <= n-k;j ++)            ans = max(ans,xx[i][m] + lx[j] + yx[j+k][m]);    for(int i = k;i <= m;i ++)        for(int j = i+k;j <= m-k;j ++)            ans = max(ans,xx[n][i] + ly[j] + xy[n][j+k]);    return ;}int main(){    n = read();    m = read();    k = read();    o = max(n,m);    for(int i = 1;i <= n;i ++)        for(int j = 1;j <= m;j ++)            a[i][j] = read();            s[i][j] = nico(i,j);    dp();    sol();    cout << ans;    return 0;}/*6 6 21 0 1 0 1 20 1 0 2 2 11 2 0 2 2 00 1 1 0 2 22 2 1 2 1 10 2 0 0 1 2*/
0 0
原创粉丝点击