题目描述
探宝的旅程仍然继续中,由于你的帮助,小可可成功点燃了灯阵,避过了许多致命的陷阱,终于来到了宫殿的正厅中。大厅的地面是由一块块大小一致的正方形石块组成的,这些石块分为黑、白两色,组成了一个 \(m\times n\) 的矩形,在其中一个石块的下面就是通往藏宝库的通道。小可可不可能一个一个石块的尝试,因为有些石块安装了机关,一碰就会触发,整个宫殿也随之倒塌。根据藏宝图记载,通道在某一特定的区域中,这个区域是一个由数个石块组成的面积不为 \(0\) 的小矩形,它的四条边与大厅地面的边平行。如果对整个大厅地面任意划分矩形,那么在所有矩形中,这个区域的黑色石块数目减去白色石块数目所得的差是最大的。
小可可希望和你分工,由他来选择区域,你来计算黑、白两色石块的数目差 \(s\)。这样就能快速而准确的确认通道所在的区域。藏宝图上说这个区域中的石块都没有安装机关,只要确定了区域,就一定能找到通道。宝藏就在眼前了,加油吧!
(假设用 1
表示黑色石块,用 0
表示白色石块)
输入格式
输入文件的第一行为两个整数 \(m,n\)。
以下 \(m\) 行,每行 \(n\) 个字符,每个字符都是 0
或 1
。
输出格式
输出文件仅一个数,表示所有可能的区域中 \(s\) 值(见前文描述)最大的一个,输出这个值即可。
3 4
1011
1111
1111
10
4 5
10110
01111
11110
10101
8
数据规模与约定
- 对于 \(50\%\) 的数据,\(1\le m,n\le 200\)。
- 对于 \(100\%\) 的数据,\(1\le m,n\le 400\)。
题目大意
挺难理解的,仔细阅读后大意如下:
给定一个 \(n\) 行 \(m\) 列的仅包含 0
和 1
的矩形,请求出区域内 1
的个数减去 0
的个数的最大值。
\(n,m\leq 400\)。
思路分析
显然可以用二维前缀和维护,枚举所有区域,求出该区域的答案求 \(\max\) 即可。
具体地,我们先用二维前缀和 \(sum_{i,j}\) 求出前 \(i\) 行前 \(j\) 列的 1
的个数(也得到了 0
的个数),根据容斥原理,有通项公式:
再枚举区域的左上角 \((u,l)\) 和右下角 \((d,r)\),该区域的答案为:
这样,枚举 \(u,l,d,r\),时间复杂度显然为 \(\mathcal{O(n^4)}\),不能通过。
\(\texttt{50pts TLE code}\)
/*Written by smx*/
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define QAQ cout<<"QAQ\n";
const int MAXN=4e2+5,inf=1e18,mod=1e9+7;
int sum[MAXN][MAXN];
int n,m,ans;
signed main(){//freopen(".in","r",stdin);//freopen(".out","w",stdout);ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);cin>>n>>m;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){char t;cin>>t;sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+t-'0';}}for(int u=1;u<=n;u++){for(int l=1;l<=m;l++){for(int d=u+1;d<=n;d++){for(int r=l+1;r<=m;r++){int s1=sum[d][r]-sum[d][l-1]-sum[u-1][r]+sum[u-1][l-1];int s0=(d-u+1)*(r-l+1)-s1;ans=max(ans,s1-s0);}}}}cout<<ans;return 0;
}
可以使用最大子段和来优化前缀和。我们对每一行求一个一维的前缀和,显然第 \(i\) 行前 \(j\) 个的通项公式为:
接着枚举区域,枚举区域的 \(l\) 和 \(r\),求出区域内每一行的和 \(f_i\),接下来对和求最大子段和,为:
对 \(f_i\) 求 \(\max\)。
这样,时间复杂度进化为 \(\mathcal{O(n^3)}\),可以通过。
\(\texttt{code}\)
/*Written by smx*/
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define QAQ cout<<"QAQ\n";
const int MAXN=4e2+5,inf=1e18,mod=1e9+7;
int n,m,ans=-inf;
int sum[MAXN][MAXN],f[MAXN];
signed main(){//freopen(".in","r",stdin);//freopen(".out","w",stdout);ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);cin>>n>>m;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){char c;cin>>c;sum[i][j]=sum[i][j-1]+(c=='1'?1:-1);}}for(int j1=1;j1<=m;j1++){for(int j2=j1;j2<=m;j2++){for(int i=1;i<=n;i++){f[i]=sum[i][j2]-sum[i][j1-1];}for(int i=1;i<=n;i++){f[i]=max(f[i],f[i-1]+f[i]);ans=max(ans,f[i]);}}}cout<<ans;return 0;
}