4.最大子矩阵
【题目描述】
给你一个 N*M 的 01 矩阵,每次你可以交换任意两列,你可以交换无限多次。你要通过
交换操作,求出最大的全 1 子矩阵的面积,所谓面积就是指包含的 1 的个数。
【输入文件】
第一行两个正整数 N 和 M。
接下来 N 行,每行包含 M 个字符’0’或’1’,描述一个矩阵。
【输出文件】
输出仅一行,为最终的最大全 1 子矩阵的面积。
【样例输入】
10 6
001010
111110
011110
111110
011110
111111
110111
110111
000101
010101
【样例输出】
21
【样例解释】
你可以通过交换,将 2,4,5 三列交换至相邻位置,行从 2-8,列为 2,4,5 三列,所以
答案为 7*3=21。
【数据规模】
对于 10%的数据,1<=M<=5;
另有 20%的数据,1<=N,M<=1024;
对于 100%的数据,1<=N<=15000,1<=M<=1500;
题解:
这题思路很神奇啊。
对于M很小的数据,我们直接M!阶乘枚举列的可能,然后套用经典的最大子矩阵的O(N*M)的算法就行了。
我们逐行考虑,对于每一行的M个位置,都可以得到它向上延伸的连续的’1’的个数,我们称之为”高度”;显然,如果我们将该行的高度从大到小排序,那么从左往右扫一遍就可以统计出以该行为底边的最大子矩阵的面积了。于是,我们就可以得到一个O(N*M*log(M))的算法了;我们对它进行优化,考虑到当行增加1以后,高度要么增加1,要么变成0,于是我们将变成0的高度放到最后就行了,用两个指针实现就可以了,时间复杂度O(N*M)
代码:
const
maxm =1505;
var
s :ansistring;
a,b,h,p :array[0..maxm] of longint;
n,m,i,j :longint;
ans,cnt,dnt,t :longint;
begin
assign(input,'logs.in');reset(input);
assign(output,'logs.out');rewrite(output);
readln(n,m);
ans:=0;
for j:=1 to m do p[j]:=j;
for i:=1 to n do
begin
readln(s);
cnt:=0;
dnt:=m+1;
for j:=1 to m do
if s[p[j]]='1' then
begin
inc(cnt);
a[cnt]:=h[j]+1;
b[cnt]:=p[j];
if cnt*a[cnt]>ans then ans:=cnt*a[cnt];
end else
begin
dec(dnt);
a[dnt]:=0;
b[dnt]:=p[j];
end;
move(a,h,6008);
move(b,p,6008);
end;
writeln(ans);
close(input);close(output);
end.