https://www.nowcoder.com/acm/contest/131/B
2.矩阵
时间限制:C/C++ 4秒,其他语言8秒空间限制:C/C++ 262144K,其他语言524288K64bit IO Format: %lld
题目描述
矩阵 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
输出描述:
输出满足以上条件的最大子矩阵和。
示例1
输入
5 5 3 3 4
0 0 10 0 0
3 4 0 2 3
-1 3 0 -8 3
0 0 32 -9 3
3 0 45 3 0
输出
82
示例2
输入
2 2 2 2 2
-1 -1
-1 -1
输出
0
想法:
原本用DFS做的,只过了28%,可能遍历不了所有子矩阵?百度一下都是用单调栈做的。又来学习一波了。
思路:
1、输入时,记录行上的前缀和,前缀0数。
2、三个for循环枚举子矩阵的上i、下j、右边界k。
3、用数组和L、R两个位置变量模拟单调栈。L:可用、满足上述条件的左边界,R:栈顶,now:到k的总前缀和。
更新子矩阵:先移动L到满足条件的左边界位置。ans = max(ans, now-ts[q[L]]),取最值。维护单调栈。
单调栈(或者说单调队列):
https://blog.csdn.net/linulysses/article/details/5771084
https://blog.csdn.net/wubaizhe/article/details/70136174
AC代码:
详细注释了网上别人的代码(哎,我现在就这水平)
https://blog.csdn.net/jaihk662/article/details/80947179
#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]; //第i行上的前缀和
s0[i][j] = s0[i][j-1]; //第i行上的前缀0数量
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; //子矩阵的和,0的个数
L = R = 0;
q[R++] = 0; //单调栈(用数组和L、R两个位置变量模拟)
for(k=1;k<=n;k++)
{
now += sum[k][j]-sum[k][i-1]; //第k行上,i~j的区间和
zero += s0[k][j]-s0[k][i-1]; //0个数
ts[k] = now; //存子矩阵的区间和
t1[k] = zero; //0个数
while(L<R && q[L]+c<k) //找子矩阵行数没超的L的位置,L即子矩阵的左边界,L前移就是淘汰单调栈里不可用的值
L++;
while(L<R && zero-t1[q[L]]>e) //子矩阵0的个数没超
L++;
ans = max(ans, now-ts[q[L]]); //取子矩阵的最值
while(L<R && ts[q[R-1]]>=now) //维护单调栈,R后移就是出栈
R--;
q[R++] = k; //入栈
}
}
}
printf("%lld\n", ans);
return 0;
}
/*
3 3 100 100 2
1 0 1
0 2 0
1 0 1
*/
本人的dfs(WA)。。。
#include <stdio.h>
#include <iostream>
using namespace std;
int R,C,X,Y,Z;
long long a[501][501];
long long dfs(int x,int y,int lx,int ly,long long n,int zn)
{
long long p,q;
if(lx < X-1 && x<R && zn<=Z){
int znx = zn;
int nx = n;
for(int i=0;i<ly;i++){
nx += a[x+1][y+i];
if(a[x+1][y+i] == 0){
znx++;
}
if(znx > Z){
p = n;
break;
}
}
if(znx <= Z){
p = dfs(x+1,y,lx+1,ly,nx,znx);
}
}else{
p = n;
}
//cout << "p:"<<p<<" " << x << " " << y << " " << lx << " " << ly <<endl;
if(ly < Y-1 && y<C && zn<=Z){
int zny = zn;
int ny = n;
for(int i=0;i<lx;i++){
ny += a[x+i][y+1];
if(a[x+i][y+1] == 0){
zny++;
}
if(zny > Z){
q = n;
break;
}
}
if(zny <= Z){
q = dfs(x,y+1,lx+1,ly,ny,zny);
}
}else{
q = n;
}
//cout << "q:"<<q<<" " << x << " " << y+1 << " " << lx << " " << ly <<endl;
if(p > q){
return p;
}else{
return q;
}
}
int main()
{
cin >> R >> C >> X >> Y >> Z;
for(int i=0;i<R;i++){
for(int j=0;j<C;j++){
cin >> a[i][j];
}
}
long long max = 0;
for(int i=0;i<R;i++){
for(int j=0;j<C;j++){
long long t;
if(a[i][j] == 0){
t = dfs(i,j,1,1,a[i][j],1);
}else{
t = dfs(i,j,1,1,a[i][j],0);
}
if(max < t){
max = t;
//cout << i << " " << j << " " << t <<endl;
}
}
}
cout << max;
}