1. 程式人生 > >【花樣列舉】bzoj 1177 apio2009採油區域

【花樣列舉】bzoj 1177 apio2009採油區域

這題正解還真是列舉……不過這個列舉很厲害的說
最暴力就是列舉三個正方形的位置,不過一定不會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 2000
using 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 2 1 0 1 0 1 2 0 1 0 2 2 1 1 2 0 2 2 0 0 1 1 0 2 2 2 2 1 2 1 1 0 2 0 0 1 2 */