题意:
矩阵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 协议 ,转载请注明出处!