题意:

矩阵M包含R行C列。

请寻找一个子矩阵,使得这个子矩阵的和最大,且满足以下三个条件:

子矩阵的行数不能超过X行。

子矩阵的列数不能超过Y列。

子矩阵中0的个数不能超过Z个。

请输出满足以上条件的最大子矩阵和。

思路:

首先考虑一个问题:求一个序列的长度不超过m的最大连续子序列。

利用前缀和可知,

pre[i]是确定的,想让dp[i]尽可能大,就得让pre[k]尽可能小。

所以可以用一个单调队列来维护pre[k]的最小值。

单调队列如何维护呢?

让这个单调队列保持从队首到队尾从小到大,因为长度不能超过m,所以如果队首的id比当前大m以上的话,就要弹出,然后把自己加入到这个队列里面,若它破坏了原队列的单调性,那么删除队尾元素,并继续比较队尾与新元素,直到找到一个队尾小于新元素时,将新元素插入到队尾。被删除的元素既比新元素小,又会比新元素先超出m,因此肯定不会成为答案。这个操作不断维护了队列中的最值。

再来看这道题目,这里是一个矩阵,所以对于行数不能超过x行的条件,可以进行枚举,然后就可以把它转化成上面那个问题了,这里还有一个限制条件0的数量,可以在队首元素弹出的时候进行判断。

代码:

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<math.h>
#include<time.h>
#include<stdlib.h>
#include<queue>
#include<stack>
#include<set>
#include<map>
#include<vector>
#include<algorithm>
#define MOD 1000000007
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
typedef long long ll;
ll a[510][510];
ll sum[510];//每列的和
ll pre[510];//每列的前缀和
int cnt[510];//每列0的个数
int bef[510];//每列的前缀0的个数
ll dp[510];//以i为结尾的最大子段和
struct node
{
    int id,num;
    ll val;
};
deque<node>q;
int main()
{
    int r,c,x,y,z;
    scanf("%d%d%d%d%d",&r,&c,&x,&y,&z);
    for(int i=1;i<=r;i++)
    for(int j=1;j<=c;j++)
    scanf("%lld",&a[i][j]);
    ll ans=0;
    for(int i=1;i<=r;i++)
    {
        memset(sum,0,sizeof(sum));
        memset(cnt,0,sizeof(cnt));
        for(int j=i;j<=min(r,i+x-1);j++)
        {
            pre[0]=0;bef[0]=0;
            for(int k=1;k<=c;k++)
            {
                sum[k]+=a[j][k];
                if(a[j][k]==0)cnt[k]++;
                pre[k]=pre[k-1]+sum[k];
                bef[k]=bef[k-1]+cnt[k];
            }
            /*printf("i=%d j=%d\n",i,j);
             for(int k=1;k<=c;k++)
             printf("%lld ",pre[k]);
             printf("\n");
             for(int k=1;k<=c;k++)
             printf("%d ",bef[k]);
             printf("\n");*/
            q.clear();
            node temp;
            temp.id=0;temp.num=bef[0];temp.val=pre[0];
            q.push_back(temp);
            for(int k=1;k<=c;k++)
            {
                while(!q.empty()&&(k-q.front().id>y||bef[k]-q.front().num>z))
                {
                    //printf("(%d,%d)\n",q.front().id,q.front().num);
                    q.pop_front();
                }
                if(!q.empty())dp[k]=pre[k]-q.front().val;
                //printf("k=%d pre[k]=%lld front=%lld\n",k,pre[k],q.front().val);
                ans=max(ans,dp[k]);
                temp.id=k;temp.num=bef[k];temp.val=pre[k];
                while(!q.empty()&&temp.val<q.back().val)
                q.pop_back();
                q.push_back(temp);
            }
            /*for(int k=1;k<=c;k++)
             printf("%lld ",dp[k]);
             printf("\n");*/
        }
    }
    printf("%lld\n",ans);
    return 0;
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

BAPC2014FinalE 线段树 Previous
BAPC2014PreliminaryH 二分+贪心 Next