单调队列+前缀和 7.31 牛客wannafly19 B

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;
}

猜你喜欢

转载自blog.csdn.net/KeeeepGO/article/details/81318583
今日推荐