行和列的问题是相对独立的,所以我们不妨先考虑列操作。
我们可以把每一行的异或和写在行首,称为第零列。那么我们每一次列操作就是交换某一列和第零列。
那么我们可以通过三次操作达到交换任意两列的效果。
由于第零列实际上不参与美丽度的计算,所以我们可以去掉一行的代价。
那么我们的问题就变成了,可以删掉一列,列顺序可以重排的最小美丽度。
对于这个问题我们用状压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; }
|