行和列的问题是相对独立的,所以我们不妨先考虑列操作。

我们可以把每一行的异或和写在行首,称为第零列。那么我们每一次列操作就是交换某一列和第零列。

那么我们可以通过三次操作达到交换任意两列的效果。

由于第零列实际上不参与美丽度的计算,所以我们可以去掉一行的代价。

那么我们的问题就变成了,可以删掉一列,列顺序可以重排的最小美丽度。

对于这个问题我们用状压DP在 $O(n^2 \cdot 2^n)$ 内解决。

设 $f(i,s)$ 表示 已包含列的状态为 $s$,最后一次加入的列为 $i$ 的最小美丽度(这里的美丽度只考虑了每一列之间的)。

转移枚举 $j$ 表示上一次加入的列即可。

对于行,同理。

但是我们可能选择删除了某一行,那么删掉的这一行对列与列之间的美丽度也是有影响的。

所以我们改一下状态,增加一维删掉的行 $t$,类似转移即可。

最后答案枚举 $i,j,ii,jj$ 分别表示:删掉的行,删掉的列,最后加入的行,最后加入的列,求最小值即可。

时间复杂度 $O(n^3 \cdot 2^n)$。

具体转移与实现细节详见代码:

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
#include<bits/stdc++.h>
using namespace std;
const int maxn=15;
const int maxm=1<<16;
int n,m;
int a[maxn+5][maxn+5];
int f[maxn+5][maxn+5][maxm+5];
int g[maxn+5][maxn+5][maxm+5];
int fs[maxn+5][maxn+5];
int gs[maxn+5][maxn+5];
inline void Solve(){
scanf("%d%d",&n,&m);
memset(a,0,sizeof(a));
memset(f,0x3f,sizeof(f));
memset(g,0x3f,sizeof(g));
memset(fs,0,sizeof(fs));
memset(gs,0,sizeof(gs));
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",&a[i][j]);
a[0][j]^=a[i][j];
a[i][0]^=a[i][j];
a[0][0]^=a[i][j];
}
}
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)
for(int k=0;k<=m;k++)
fs[i][j]+=abs(a[i][k]-a[j][k]);
for(int i=0;i<=m;i++)
for(int j=0;j<=m;j++)
for(int k=0;k<=n;k++)
gs[i][j]+=abs(a[k][i]-a[k][j]);
int tpn=(1<<(n+1))-1,tpm=(1<<(m+1))-1;
for(int i=0;i<=n;i++)
for(int j=0;j<=m;j++)
f[j][i][1<<i]=g[i][j][1<<j]=0;
for(int t=0;t<=m;t++){
for(int s=0;s<=tpn;s++){
for(int i=0;i<=n;i++){
for(int j=0;j<=n;j++){
if(i==j) continue;
if(!(s&(1<<i))) continue;
if(!(s&(1<<j))) continue;
f[t][j][s]=min(f[t][j][s],f[t][i][s^(1<<j)]
+fs[i][j]-abs(a[i][t]-a[j][t]));
}
}
}
}
for(int t=0;t<=n;t++){
for(int s=0;s<=tpm;s++){
for(int i=0;i<=m;i++){
for(int j=0;j<=m;j++){
if(i==j) continue;
if(!(s&(1<<i))) continue;
if(!(s&(1<<j))) continue;
g[t][j][s]=min(g[t][j][s],g[t][i][s^(1<<j)]
+gs[i][j]-abs(a[t][i]-a[t][j]));
}
}
}
}
int ans=INT_MAX;
for(int i=0;i<=n;i++)
for(int j=0;j<=m;j++)
for(int ii=0;ii<=n;ii++)
for(int jj=0;jj<=m;jj++)
ans=min(ans,f[j][ii][tpn^(1<<i)]+g[i][jj][tpm^(1<<j)]);
printf("%d\n",ans);
}
signed main(){
int T;
scanf("%d",&T);
while(T--) Solve();
return 0;
}