[2007 AHOI]宝库通道 (Treasure Passage) 题解

1|0题目描述

探宝的旅程仍然继续中,由于你的帮助,小可可成功点燃了灯阵,避过了许多致命的陷阱,终于来到了宫殿的正厅中。大厅的地面是由一块块大小一致的正方形石块组成的,这些石块分为黑、白两色,组成了一个 m×n 的矩形,在其中一个石块的下面就是通往藏宝库的通道。小可可不可能一个一个石块的尝试,因为有些石块安装了机关,一碰就会触发,整个宫殿也随之倒塌。根据藏宝图记载,通道在某一特定的区域中,这个区域是一个由数个石块组成的面积不为 0 的小矩形,它的四条边与大厅地面的边平行。如果对整个大厅地面任意划分矩形,那么在所有矩形中,这个区域的黑色石块数目减去白色石块数目所得的差是最大的。

小可可希望和你分工,由他来选择区域,你来计算黑、白两色石块的数目差 s。这样就能快速而准确的确认通道所在的区域。藏宝图上说这个区域中的石块都没有安装机关,只要确定了区域,就一定能找到通道。宝藏就在眼前了,加油吧!

(假设用 1 表示黑色石块,用 0 表示白色石块)

2|0输入格式

输入文件的第一行为两个整数 m,n
以下 m 行,每行 n 个字符,每个字符都是 01

3|0输出格式

输出文件仅一个数,表示所有可能的区域中 s 值(见前文描述)最大的一个,输出这个值即可。

3 4 1011 1111 1111
10
4 5 10110 01111 11110 10101
8

4|0数据规模与约定

  • 对于 50% 的数据,1m,n200
  • 对于 100% 的数据,1m,n400

4|1题目大意

挺难理解的,仔细阅读后大意如下:

给定一个 nm 列的仅包含 01 的矩形,请求出区域内 1 的个数减去 0 的个数的最大值。

n,m400

4|2思路分析

显然可以用二维前缀和维护,枚举所有区域,求出该区域的答案求 max 即可。

具体地,我们先用二维前缀和 sumi,j 求出前 i 行前 j 列的 1 的个数(也得到了 0 的个数),根据容斥原理,有通项公式:

sumi,j=sumi1,j+sumi,j1sumi1,j1

再枚举区域的左上角 (u,l) 和右下角 (d,r),该区域的答案为:

ans=sum[d][r]sum[d][l1]sum[u1][r]+sum[u1][l1]

这样,枚举 u,l,d,r,时间复杂度显然为 O(n4),不能通过。

1|050pts 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 个的通项公式为:

sumi,j=sumi,j1+ai,j

接着枚举区域,枚举区域的 lr,求出区域内每一行的和 fi,接下来对和求最大子段和,为:

fi=max(fi,fi1+fi)

fimax

这样,时间复杂度进化为 O(n3),可以通过。

4|3code

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

__EOF__

本文作者shimingxin1007
本文链接https://www.cnblogs.com/shimingxin1007/p/18682694.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   shimingxin1007  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示