505

505

Created by LXC on Sat Feb 24 22:58:36 2024

https://codeforces.com/problemset/problem/1391/D

ranting: 2000

tag: bitmasks, brute force, constructive algorithms, dp, greedy, implementation

problem

给出一个 $n \times m$ 的 $01$ 矩阵,如果每个长宽都为偶数的正方形子矩阵内 $1$ 的个数都为奇数,则这是一个“好的”矩阵。如果能把矩阵改成“好的”,问最少改多少个数。如果不能,输出 $-1$。

solution

首先对于4x4的矩阵,不可能存在好的。因为4x4矩阵分成互不重叠的4个2x2矩阵,由于各子矩阵为好的则1的个数是奇数个。那么四个奇数的和必定是偶数。

给出的矩阵形状只有$1\times m, 2\times m, 3\times m, n \times 1, n\times 2, n\times 3$需要计算。

注意到边长较小的不超过3,我们可以用一个二进制数来表示这行或列的01选取情况。然后用状压dp来做就行。

例如对于一个$3\times m$的矩阵。将每一列i编码成二进制数$v_i$。 设$f_{i,mask}$为0到i列中,第i列为mask的情况下,成为好的矩阵所需的最少修改次数。转移方程$f_{i,mask} = bct(mask \oplus v_i)+\max f_{i-1, mask’}$,其中$bct$为取二进制数的1的个数。$mask$与$mask’$这相邻的两列中$2\times 2$矩阵必须是”好的”。可以事先打表预处理。

code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112

#include <bits/stdc++.h>
#define SINGLE_INPUT
#define ll long long
#define ull unsigned long long
#define N 500005
#define MOD 998244353
using namespace std;

template<class t,class u> ostream& operator<<(ostream& os,const pair<t,u>& p) {
return os<<'['<<p.first<<", "<<p.second<<']';
}
template<class t> ostream& operator<<(ostream& os,const vector<t>& v) {
os<<'['; int s = 1;
for(auto e:v) { if (s) s = 0; else os << ", "; os << e; }
return os<<']';
}
template<class t,class u> ostream& operator<<(ostream& os,const map<t,u>& mp){
os<<'{'; int s = 1;
for(auto [x,y]:mp) { if (s) s = 0; else os << ", "; os<<x<<": "<<y; }
return os<<'}';
}

void sol() {
int n, m;
cin >> n >> m;
vector<string> g(n);
for (auto& i:g) cin >> i;
int sz = max(n, m);
int mn = min(n, m);
if (mn >= 4) {
cout << "-1\n";
return ;
}

vector<int> v(sz);
if (n>m) {
for (int i=0; i<n; i++) {
for (int j=0; j<m; j++) {
v[i] = v[i]*2+g[i][j]-'0';
}
}
} else {
for (int j=0; j<m; j++) {
for (int i=0; i<n; i++) {
v[j] = v[j]*2+g[i][j]-'0';
}
}
}
// cout << v << endl;
auto bct = [](int x) {
int rt = 0;
for (int i=x; i; i=i&(i-1)) rt++;
return rt;
};
if (mn == 1) {
cout << "0\n";
} else if (mn == 2) {
vector<vector<int>> tr = {{1, 2}, {0, 3}, {0, 3}, {1, 2}};
vector<vector<int>> dp(sz, vector<int>(4, n*m));
for (int i=0; i<4; i++) {
dp[0][i] = bct(i^v[0]);
}
for (int i=1; i<sz; i++) {
for (int j=0; j<4; j++) {
dp[i][j] = min(dp[i-1][tr[j][0]], dp[i-1][tr[j][1]])+bct(j^v[i]);
}
}
// cout << dp << endl;
cout << *min_element(dp[sz-1].begin(), dp[sz-1].end()) << "\n";

} else {
// 000
// 001
// 010
// 011
// 100
// 101
// 110
// 111
vector<vector<int>> tr = {{2, 5}, {3, 4}, {0, 7}, {1, 6}, {1, 6}, {0, 7}, {3, 4}, {2, 5}};
vector<vector<int>> dp(sz, vector<int>(8, n*m));
for (int i=0; i<8; i++) {
dp[0][i] = bct(i^v[0]);
}
for (int i=1; i<sz; i++) {
for (int j=0; j<8; j++) {
dp[i][j] = min(dp[i-1][tr[j][0]], dp[i-1][tr[j][1]])+bct(j^v[i]);
}
}
// cout << dp << endl;
cout << *min_element(dp[sz-1].begin(), dp[sz-1].end()) << "\n";
}
}

int main() {
cout << setprecision(15) << fixed;
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
#ifndef SINGLE_INPUT
int t;
cin >> t;
while (t--) {
sol();
}
#else
sol();
#endif
return 0;
}