给一个01矩阵,求一个需要修改次数最小的子矩阵(长度至少为4,高度至少为5)的修改次数。
一次修改可以把单个0改为1或者1改为0。
修改后的矩阵需满足,中间全为0,四边为1,四个角随意。
枚举子矩阵的上下界和右边界,左边界动态维护。
f[k]:以i,j为上下界,k为右边界的修改次数。(不考虑右边界)
f
[
k
]
=
m
i
n
(
f
[
k
−
1
]
+
加
入
第
k
行
需
要
的
修
改
次
数
,
以
i
、
j
、
k
−
3
、
k
为
边
界
的
矩
阵
的
修
改
次
数
)
f[k]=min(f[k-1]+加入第k行需要的修改次数,以i、j、k-3、k为边界的矩阵的修改次数)
f[k]=min(f[k−1]+加入第k行需要的修改次数,以i、j、k−3、k为边界的矩阵的修改次数)
每次用当前的
f
[
k
]
f[k]
f[k]+右边界需要的修改次数更新ans。
#include<bits/stdc++.h>
using namespace std;
#define read(a) scanf("%d",&a)
#define maxn 400
int n,m;
int r[maxn+5][maxn+5];
int a[maxn+5][maxn+5],b[maxn+5][maxn+5]; //a:1 b:0
int f[maxn+5];
int find1(int i,int j,int k) {
return a[j][k]-a[i-1][k];
}
int find0(int i,int j,int k) {
return b[j][k]-b[i-1][k];
}
int main() {
int T;
read(T);
while(T--) {
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
read(n),read(m);
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
char x;
do{x=getchar();}while(x!='0'&&x!='1');
r[i][j]=x-'0';
a[i][j]=r[i][j]+a[i-1][j];
b[i][j]=1-r[i][j]+b[i-1][j];
}
}
int ans=(int)1e9;
for(int i=1;i<=n;i++) {
for(int j=i+4;j<=n;j++) {
f[3]=(int)1e9;
for(int k=4;k<=m;k++) {
int x=find0(i+1,j-1,k-3)+!r[i][k-2]+!r[i][k-1]+!r[j][k-2]+!r[j][k-1]+find1(i+1,j-1,k-2)+find1(i+1,j-1,k-1);
f[k]=min(f[k-1]+find1(i+1,j-1,k-1)+!r[i][k-1]+!r[j][k-1],x);
ans=min(ans,f[k]+find0(i+1,j-1,k));
}
}
}
printf("%d\n",ans);
}
return 0;
}