题目要求每一个平方子矩阵里1都为奇数
那么我们先分析,最小的子矩阵肯定是
2
×
2
2\times 2
2×2,然后就是
4
×
4
4\times 4
4×4 的矩阵了,然而假如
2
×
2
2\times 2
2×2 的矩阵里,1都是奇数个,那
4
×
4
4\times 4
4×4 矩阵里,怎么都是偶数,所以min(n,m)肯定不能大于等于4,否则直接输出-1,那么我们现在可以确定n和m至少有一个是小于等于3的了。
再继续分析,我们设n是小的,m是大的(总之这个矩阵你可以随便转90°,要是n比m大你就转一下呗),假如n是1,就一行,啥子矩阵都没,那直接输出0喽。
假如n是2呢?比如现在,
1 0 1
0 1 0
你会发现因为n是2,所以子矩阵的高就是2,那么枚举子矩阵的左右边界就行了,第一列的1的数量是1个,第二列的数量是1个,第一列和第二列可以拼成一个
2
×
2
2\times 2
2×2 矩阵,那就是把第一列或者第二列修改1次,然后第二列又和第三列可以拼成一个子矩阵,那么就是第二列或者第三列修改一次,现在长度是3,那把长度拓展后呢?
这很明显是个dp问题。
n是3的时候呢?
1 0 1
0 1 1
0 1 0
比如这个,一共可以拼出4个子矩阵,左上,左下,右上,右下,这样的话就是相当于在n=2的基础上再额外加个判断,n=2只需要判断某一列的第一行和第二行,n=3的时候需要判断某一列的第一行和第二行以及第二行和第三行,这里我们可以通过状压来省去很多步骤,3行,每行有0或者1两种状态,所以0~7可以依次对应一下8种情况。
0 1 2 3 4 5 6 7
0 1 0 1 0 1 0 1
0 0 1 1 0 0 1 1
0 0 0 0 1 1 1 1
这样就把3行直接压成了一行,并且比较第一行和第二行的时候,直接比较a[i]%4即可,比较第二行和第三行的时候,直接比较a[i]>>1即可,如何统计两列中1个总个数呢?
1 0 1
0 1 1
0 1 0
就比如第一列和第二列现在拼子矩形,可以拼出
1 0 和 0 1
0 1 0 1
那么用二进制表示就是1,2和0,3,
1
∧
2
=
3
1 \land 2 =3
1∧2=3,
0
∧
3
=
3
0 \land 3=3
0∧3=3,他们俩个合并后都是3,而3的1共有2个,所以第一列和第二列不能直接组合,这个时候枚举第一列和第二列各自什么状态才能组合,最多需要枚举
8
×
8
8 \times 8
8×8 次
一共需要枚举m位,所以复杂度是
m
×
8
×
8
m \times 8 \times 8
m×8×8,绰绰有余。
具体可以见代码。
#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<n;i++)
#define per(i,a,n) for(int i=n-1;i>=a;i--)
typedef long long ll;
typedef double db;
typedef pair<int,int> PII;
typedef vector<int> VI;
const ll mod=1000000007;
const int maxn=5000005;
const int inf=0x3f3f3f3f;
const int INF=0x7f7f7f7f;
ll gcd(ll a,ll b){ return b?gcd(b,a%b):a;}
#define ms(a) memset(a,0,sizeof(a))
#define mss(a) memset(a,-1,sizeof(a))
#define msi(a) memset(a,inf,sizeof(a))
#define iossync ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
// head
string s1;
int k[10];
int a,b[maxn];
int dp[maxn][10];
int n,m;
bool check(int x,int y)
{
return k[(x^y)]&1;
}
void fact1()
{
rep(i,0,8)
dp[0][i]=k[b[0]^i];//转换成i形式需要花费多少
rep(i,1,m)
{
rep(j,0,8)
{
rep(z,0,8)
{
if(check(j>>1,z>>1) && check(j%4,z%4))
{//上下同时满足奇数
dp[i][z]=min(dp[i][z],dp[i-1][j]+k[(b[i])^z]);
}
}
}
}
rep(i,0,8)
{
dp[m-1][0]=min(dp[m-1][i],dp[m-1][0]);
}
cout<<dp[m-1][0]<<"\n";
}
void fact()
{
rep(i,0,4)
dp[0][i]=k[b[0]^i];
rep(i,1,m)
{
rep(j,0,4)
{
rep(z,0,4)
{
if(check(j,z))
{//一共就2行,比较一次就好
dp[i][z]=min(dp[i][z],dp[i-1][j]+k[(b[i])^z]);
}
}
}
}
rep(i,0,4)
{
dp[m-1][0]=min(dp[m-1][i],dp[m-1][0]);
}
cout<<dp[m-1][0]<<"\n";
}
int main()
{
msi(dp);
rep(i,0,8)
k[i]=k[i>>1]+(i&1);//预处理0~8里各包含几个1
cin>>n>>m;
if(min(n,m)>=4)
{
while(n--)
cin>>s1;
cout<<"-1\n";
return 0;
}
//默认n<m
if(m<n)
{
rep(i,0,m)
{
rep(j,0,n)
{
scanf("%1d",&a);
b[j]|=(a<<i);
}
}
swap(n,m);
}
else
{
rep(i,0,n)
{
rep(j,0,m)
{
// scanf("%1d",&a[i][j]);
scanf("%1d",&a);
b[j]|=(a<<i);
}
}
}
switch(n)
{
case 1:{
cout<<"0\n";
break;
}
case 2:{
fact();
break;
}
case 3:{
fact1();
break;
}
}
return 0;
}