1. 程式人生 > >【APIO2009T1】採油區域-分類討論+動態規劃

【APIO2009T1】採油區域-分類討論+動態規劃

測試地址:採油區域
題目大意:給定一個M×N的僅含非負整數的矩陣,要求找出三個不相交的K×K區域使區域內整數之和最大,輸出這個最大值。
做法:這道題目需要用到分類討論+動態規劃。
因為不能相交,所以三個正方形區域的位置關係一定可以被歸為下列情況之一:
1.三個正方形被一條橫向的分界線間隔,可能的情況有:
(1)上面有兩個,下面有一個。
(2)上面有一個,下面有兩個。
2.三個正方形被一條縱向的分界線間隔,可能的情況有:
(1)左邊有兩個,右邊有一個。
(2)左邊有一個,右邊有兩個。
而在同一個區域的兩個正方形要麼被一條橫向的分界線間隔,要麼被一條縱向的分界線間隔。因此,我們只需列舉分界線,按照這種方法分割區域後,每個區域再找出一個最大的正方形即可。
列舉分界線是O

(MN)的,但如果暴力算一個區域內的最大正方形,複雜度是O(K2MN)的,絕對會炸。這時候就要想辦法預處理出一些區域內最大的正方形。首先我們可以對矩陣求一個二維字首和,然後算正方形就是O(1)的了,而我們發現,上面那些情況中,要求的區域只有6種情況:4種角上的區域,還有2種是被兩條分界線隔在中間的區域。前面4種預處理就很容易了,而後面2種實際上不用完全預處理完,我們可以求出正方形右下角點在某一行(列)上時最大的正方形值,這樣在列舉分界線時就可以順便計算出某個區域中最大的正方形值。
由於沒有圖,我也解釋不太清楚了,詳細看程式碼吧。優化後的演算法複雜度為O(MN),可以說是完美的複雜度。
以下是本人程式碼:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
using namespace std;
int n,m,k;
ll map[1510][1510],sum[1510][1510]={0},bsum[1510][1510]={0};
ll mx1[1510][1510]={0},mx2[1510][1510]={0},mx3[1510][1510]={0},mx4[1510][1510
]={0}; ll mx5[1510]={0},mx6[1510]={0}; ll ans=0; int main() { scanf("%d%d%d",&m,&n,&k); for(int i=1;i<=m;i++) { int tot=0; for(int j=1;j<=n;j++) { scanf("%lld",&map[i][j]); tot+=map[i][j]; sum[i][j]=sum[i-1][j]+tot; } } for(int i=k;i<=m;i++) for(int j=k;j<=n;j++) bsum[i][j]=sum[i][j]-sum[i-k][j]-sum[i][j-k]+sum[i-k][j-k]; for(int i=k;i<=m;i++) { ll mx=0; for(int j=k;j<=n;j++) { mx=max(mx,bsum[i][j]); mx1[i][j]=max(mx1[i-1][j],mx); } } for(int i=m;i>=k;i--) { ll mx=0; for(int j=n;j>=k;j--) { mx=max(mx,bsum[i][j]); mx2[i][j]=max(mx2[i+1][j],mx); } } for(int i=k;i<=m;i++) { ll mx=0; for(int j=n;j>=k;j--) { mx=max(mx,bsum[i][j]); mx3[i][j]=max(mx3[i-1][j],mx); } } for(int i=m;i>=k;i--) { ll mx=0; for(int j=k;j<=n;j++) { mx=max(mx,bsum[i][j]); mx4[i][j]=max(mx4[i+1][j],mx); } } for(int i=k;i<=m;i++) for(int j=k;j<=n;j++) mx5[i]=max(mx5[i],bsum[i][j]); for(int j=k;j<=n;j++) for(int i=k;i<=m;i++) mx6[j]=max(mx6[j],bsum[i][j]); for(int i=k;i<=m-k;i++) for(int j=k;j<=n-k;j++) { ans=max(ans,mx1[i][j]+mx2[i+k][k]+mx3[i][j+k]); ans=max(ans,mx1[m][j]+mx2[i+k][j+k]+mx3[i][j+k]); ans=max(ans,mx1[i][n]+mx2[i+k][j+k]+mx4[i+k][j]); ans=max(ans,mx1[i][j]+mx2[k][j+k]+mx4[i+k][j]); } ll mmx1=0,mmx2; for(int i=k;i<=m-2*k;i++) { mmx1=max(mmx1,mx5[i]); mmx2=0; for(int j=i+k;j<=m-k;j++) { mmx2=max(mmx2,mx5[j]); ans=max(ans,mmx1+mmx2+mx2[j+k][k]); } } mmx1=0; for(int i=k;i<=n-2*k;i++) { mmx1=max(mmx1,mx6[i]); mmx2=0; for(int j=i+k;j<=n-k;j++) { mmx2=max(mmx2,mx6[j]); ans=max(ans,mmx1+mmx2+mx2[k][j+k]); } } printf("%lld",ans); return 0; }