矩阵(前缀和)
题目描述:
矩阵 M 包含 R 行 C 列,第 i 行第 j 列的值为 Mi,j。
请寻找一个子矩阵,使得这个子矩阵的和最大,且满足以下三个条件:
子矩阵的行数不能超过 X 行。
子矩阵的列数不能超过 Y 列。
子矩阵中 0 的个数不能超过 Z 个。
请输出满足以上条件的最大子矩阵和。
Input:
第一行输入五个整数 R,C,X,Y,Z。
接下来 R行,每行输入 C 个整数,第 i 行第 j 列的整数表示 Mi,j。
1 ≤ R,C ≤ 500.
1 ≤ X ≤ R.
1 ≤ Y ≤ C.
1 ≤ Z ≤ R x C.
-109 ≤ Mi,j ≤ 109
Output:
输出满足以上条件的最大子矩阵和。
Example:
input1:
5 2 0
2 1 1 1
2 1 1 1
3 2 1 1
4 3 1 1
5 4 1 1
output1:
82
input2:
2 2 2 2 2
-1 -1
-1 -1
output2:
0
思路:统计每列数的0的个数的前缀和,以及每列数的前缀和,3重循环,第3重循环内求满足条件的矩阵的和,具体操作见代码。
#include<bits/stdc++.h> #include<iostream> #include<cctype> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<queue> #include<stack> using namespace std; typedef unsigned long long ull; typedef long long ll; typedef pair<ll,ll> pi; #define IOS std::ios::sync_with_stdio(false) #define ls p<<1 #define rs p<<1|1 #define mod 1000000000 + 7 #define PI acos(-1.0) #define INF 1e18 #define N 666 + 5 /*********************Code*********************/ ll n,m,x,y,z,ans,a[N][N],q[N],l,r,sum[N],res[N],c[N][N]; int main(void){ IOS; cin>>n>>m>>x>>y>>z; for(ll i = 1;i <=n;i++) for(ll j = 1;j <=m;j++){ cin>>a[i][j]; } for(ll j = 1;j <=m;j++) for(ll i = 1;i <=n;i++) c[i][j] = c[i-1][j] +(a[i][j]==0); //求每一列数的0的个数的前缀和 for(ll i = 1;i <=n;i++) for(ll j = 1;j <=m;j++) a[i][j] +=a[i-1][j]; //求每一列数的前缀和 for(ll i = 0;i < n;i++){ for(ll j = i + 1;j <=min(n,i+x);j++){ //j表示从第i行开始计数,能计到多少行 q[l=r=1] = 0,sum[0] = res[0] = 0; //q[i]表示可取的列,q[l]表示当前是第几列 for(ll k = 1;k <=m;k++){ sum[k] = sum[k-1]+a[j][k] - a[i][k]; //sum[k]表示取k列,第j-i行矩阵的前缀和 res[k] = res[k-1]+c[j][k] -c[i][k]; //res[k]表示取k列,第j-i行的0的个数 while(l<=r&&(k-q[l]>y||res[k]-res[q[l]]>z)) //如果在l小于、等于r的情况下,第k列到第q[l]列的列数差大于y,或者k列到q[l]列的0的个数大于z,l++,从第l列开始计数 l++; if(l<=r) //如果当前列可取 ans = max(ans,sum[k]-sum[q[l]]); while(l<=r&&sum[k]<=sum[q[r]]) //如果去第k列后,矩阵和减少,就将r--,重新记录可取的列,保证求最大值时,求得的值为正数 r--; q[++r] = k; //q[r]记录已经到达第几列 } } } cout<<ans<<endl; return 0; }