Wannafly挑戰賽19:B. 矩陣(單調棧)
阿新 • • 發佈:2019-02-05
題目描述
矩陣 M 包含 R 行 C 列,第 i 行第 j 列的值為 Mi,j。請尋找一個子矩陣,使得這個子矩陣的和最大,且滿足以下三個條件:
子矩陣的行數不能超過 X 行。
子矩陣的列數不能超過 Y 列。
子矩陣中 0 的個數不能超過 Z 個。
請輸出滿足以上條件的最大子矩陣和。
輸入描述:
第一行輸入五個整數 R,C,X,Y,Z。接下來 N 行,每行輸入 M 個整數,第 i 行第 j 列的整數表示 Mi,j。1 ≤ R,C ≤ 500.1 ≤ X ≤ R.1 ≤ Y ≤ C.1 ≤ Z ≤ R x C.-109 ≤ Mi,j ≤ 109
輸出描述:
輸出滿足以上條件的最大子矩陣和
設矩陣的大小為n*m
先n²暴力所有行的選法(注意行的長度不能超過題目要求的X)
之後求出sum[j]表示前j列所有數字之和
那麼到這就是求sum[x]-sum[y]的最大值(x∈(1, m),y∈(0, m-1),y-x<=Y)
暴力所有的x,那麼很顯然你只要找到<x的最小的y即可,也就是隻用維護一個字首最小值
但是這道題還有額外的要求:列長度不能超過Y,且0的個數不能超過Z,所以對於每個x∈(1, m),一定存在一個臨界點p,滿足矩形不能跨過第p列(這個也很容易求,略),也就是要把前面的字首和全部丟掉
那。。。這不就是單調棧的經典應用嘛,搞定
#include<stdio.h> #include<algorithm> using namespace std; #define LL long long LL a[505][505], sum[505][505], q[505], s0[505][505], ts[505], t1[505]; int main(void) { LL now, ans, zero; int n, m, i, j, c, d, e, k, L, R; scanf("%d%d%d%d%d", &n, &m, &c, &d, &e); for(i=1;i<=n;i++) { for(j=1;j<=m;j++) { scanf("%lld", &a[i][j]); sum[i][j] = sum[i][j-1]+a[i][j]; s0[i][j] = s0[i][j-1]; if(a[i][j]==0) s0[i][j]++; } } ans = 0; for(i=1;i<=m;i++) { for(j=i;j<=m;j++) { if(j-i+1>=d+1) continue; now = zero = 0; L = R = 0; q[R++] = 0; for(k=1;k<=n;k++) { now += sum[k][j]-sum[k][i-1]; zero += s0[k][j]-s0[k][i-1]; ts[k] = now; t1[k] = zero; while(L<R && q[L]+c<k) L++; while(L<R && zero-t1[q[L]]>e) L++; ans = max(ans, now-ts[q[L]]); while(L<R && ts[q[R-1]]>=now) R--; q[R++] = k; } } } printf("%lld\n", ans); return 0; } /* 3 3 100 100 2 1 0 1 0 2 0 1 0 1 */