这道题目的话看似没有头绪,我们分析一下。
偶数长度平方矩阵为: 2 × 2 2 \times 2 2×2、 4 × 4 4 \times 4 4×4、 8 × 8 8 \times 8 8×8……
我们考虑是否可能同时满足所有 2 × 2 2 \times 2 2×2 的矩阵 1 1 1 的个数为奇数,所有 4 × 4 4 \times 4 4×4 的矩阵 1 1 1 的个数为奇数。
如图,每个黑色正方形是一个 2 × 2 2 \times 2 2×2 的矩阵,为了满足要求它们 1 1 1 的个数都为奇数,由 4 4 4 个 2 × 2 2 \times 2 2×2 的矩阵拼成的 4 × 4 4 \times 4 4×4 大矩阵为了复合要求也必须由奇数个 1 1 1,然后这和我们上面的东西矛盾,因为奇数 × 4 = \times 4= ×4= 偶数,所以, n ≥ 4 n\geq 4 n≥4 且 m ≥ 4 m \geq 4 m≥4 的矩阵,一定不能满足所有条件。
那么由于 n ≤ 3 n\leq 3 n≤3,所以我们分类讨论。
-
对于 n = 1 n=1 n=1 ,答案显然为 0 0 0。
-
对于 n = 2 n=2 n=2 ,显然对于连续的 2 2 2 列,这两列 1 1 1 的个数必须奇偶性不相同,因为只有奇数+偶数=奇数。所以一共就只有 2 2 2 钟情况。
- 第 1 1 1 列 1 1 1 的个数为奇数,第 2 2 2 列 1 1 1 的个数为偶数……
- 第 1 1 1 列 1 1 1 的个数为偶数,第 2 2 2 列 1 1 1 的个数为奇数……
时间复杂度 O ( n ) O(n) O(n)。
-
对于 n = 3 n=3 n=3,我们可以用状压 d p dp dp 来做,我们设 f i , j f_{i,j} fi,j 表示第 i i i 列状态为 j j j,转移的话,我们枚举前一列的合法状态,对所有可行解取个 min \min min 即可,最后不要忘记加上这 1 1 1 列修改的方格数。
不难发现,每个状态可以转移的状态数均为 2 2 2,因此此题复杂度为 O ( n ) O(n) O(n), 16 16 16 倍常数,但这是可以 A A A 的。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>inline void read(T &FF){
T RR=1;FF=0;char CH=getchar();
for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
FF*=RR;
}
int a[15][1000010],f[1000010][11];
vector<int>v[15];
bool check(int x,int y){//判断2个状态是否能相互转移
int a1=x&1,a2=(x>>1)&1,a3=(x>>2)&1;
int b1=y&1,b2=(y>>1)&1,b3=(y>>2)&1;
if((a1+a2+b1+b2)%2==0)return false;
if((a2+a3+b2+b3)%2==0)return false;
return true;
}
int work(int y,int x){//算修改了几个格子
int a1=x&1,a2=(x>>1)&1,a3=(x>>2)&1;
return (a[1][y]!=a1)+(a[2][y]!=a2)+(a[3][y]!=a3);
}
int main(){
int n,m;read(n);read(m);
if(n>=4&&m>=4){
puts("-1");
return 0;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
char ch=getchar();
for(;ch!='0'&&ch!='1';ch=getchar());
a[i][j]=(ch=='1');
}
if(n==1){
puts("0");
return 0;
}
if(n==2){
int ans1=0,ans2=0;
for(int j=1;j<=m;j++)
if((a[1][j]+a[2][j])%2!=(j&1))ans1++;
for(int j=1;j<=m;j++)
if((a[1][j]+a[2][j])%2!=((j&1)^1))ans2++;
cout<<min(ans1,ans2);
return 0;
}
for(int i=0;i<(1<<3);i++)
for(int j=0;j<(1<<3);j++)
if(check(i,j))
v[j].push_back(i);
for(int j=0;j<(1<<3);j++)f[1][j]=work(1,j);//边界
for(int i=2;i<=m;i++)
for(int j=0;j<(1<<3);j++){
f[i][j]=n*m+1;
for(auto k:v[j]){
f[i][j]=min(f[i][j],f[i-1][k]+work(i,j));
}
}
int ans=n*m+1;
for(int j=0;j<(1<<3);j++)ans=min(ans,f[m][j]);
cout<<ans;
return 0;
}