结论,异或,状压 DP 2300
Link:https://codeforces.com/problemset/problem/1993/E。
先考虑一维的情况。
若只有一维,每次操作的结果和 [AGC016D] XOR Replace 是一样的。对 \(a_i\) 进行一次操作相当于令 \(a_i:=\oplus_{1\le i\le n} a_i\),再对 \(j\) 进行一次操作相当于令 \(a_j:= a_i\)。
则题意等价于有一个长度为 \(n + 1\) 的数列 \(a\),\(a_{n + 1} = \oplus_{1\le i\le n} a_i\),可以任意交换 \(a_i\) 与 \(a_{n + 1}\),对于 \(a_1\sim a_n\) 求相邻两项差的绝对值之和的最小值。
发现数据范围很小,且贡献仅与相邻元素有关,考虑状压 DP 构造数列,记 \(f_{s, i}\) 表示当前填入的数列元素集合为 \(s\),填入的最后一个数是 \(a_i\) 时美丽值的最小值。初始化 \(f_{s, i} = \infin, f_{\{i\}, i} = 0\),则有显然的转移:
\[\forall 1\le i, j\le n + 1, i\not= j, i\in s,\ \ f_{s, i}\rightarrow f_{s \cup \{j\}, j} + |a_{i} - a_{j}| \]记全集为 \(S\),答案即为:
\[\min_{1\le i,j\le n+1, i\not= j} f_{S - \{i\}, j} \]总时间复杂度 \(O(n^2 2^n)\) 级别。
扩展到两维,发现若先进行一次行操作再进行一次列操作,等价于将交点位置修改为整个矩阵的异或和。于是考虑扩展上述做法,题意等价于有一个大小为 \((n + 1)\times (m + 1)\) 的矩阵,第 \(n+1\) 行为各列的异或和,第 \(m+1\) 列为各行的异或和(\((n + 1, m + 1)\) 即整个矩阵异或和),每次操作可以交换两行/两列,求左上角 \(n\times m\) 矩阵的美丽值的最小值。
考虑预处理任意两行/两列相邻时的贡献。发现两维的贡献是独立的,不同维的交换并不影响另一维的贡献。发现若枚举了哪一行被放在了 \(n+1\) 行上,则对列的贡献的计算就可以直接套用一维的做法了。于是考虑转移时处理 \(\operatorname{sum}(i, j)\) 表示将第 \(i\) 行第 \(j\) 列时的最小美丽值,取最小值即可。
在一维做法的基础上仅需再多枚举一维即可,总时间复杂度 \(O(n^2m 2^n + nm^2 2^m) \approx O(n^3 2^n)\) 级别。
//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 16 + 2;
const int kS = (1 << 16) + 10;
const LL kInf = 1e18;
//=============================================================
int n, m, a[kN][kN];
LL ans, all, row[kN][kN], col[kN][kN], f[kS][kN];
LL sum[kN][kN];
//=============================================================
void init() {
a[n + 1][m + 1] = 0;
for (int i = 1; i <= n; ++ i) {
a[i][m + 1] = 0;
for (int j = 1; j <= m; ++ j) {
a[i][m + 1] ^= a[i][j];
a[n + 1][m + 1] ^= a[i][j];
}
}
for (int j = 1; j <= m; ++ j) {
a[n + 1][j] = 0;
for (int i = 1; i <= n; ++ i) {
a[n + 1][j] ^= a[i][j];
}
}
for (int i = 1; i <= n + 1; ++ i) {
for (int j = 1; j <= n + 1; ++ j) {
row[i][j] = 0;
for (int k = 1; k <= m + 1; ++ k)
row[i][j] += abs(a[i][k] - a[j][k]);
}
}
for (int i = 1; i <= m + 1; ++ i) {
for (int j = 1; j <= m + 1; ++ j) {
col[i][j] = 0;
for (int k = 1; k <= n + 1; ++ k)
col[i][j] += abs(a[k][i] - a[k][j]);
}
}
}
void DP() {
for (int i = 1; i <= n + 1; ++ i) {
for (int j = 1; j <= m + 1; ++ j) {
sum[i][j] = 0;
}
}
all = (1 << (n + 1));
for (int lst = 1; lst <= m + 1; ++ lst) {
for (int s = 1; s < all; ++ s) {
for (int i = 1; i <= n + 1; ++ i) {
f[s][i] = kInf;
}
}
for (int i = 1; i <= n + 1; ++ i) f[1 << (i - 1)][i] = 0;
for (int s = 1; s < all; ++ s) {
for (int i = 1; i <= n + 1; ++ i) {
if ((s >> (i - 1) & 1) == 0) continue;
for (int j = 1; j <= n + 1; ++ j) {
if (i == j || (s >> (j - 1) & 1)) continue;
f[s | (1 << (j - 1))][j] = std::min(f[s | (1 << (j - 1))][j],
f[s][i] + row[i][j] - abs(a[i][lst] - a[j][lst]));
}
}
}
for (int i = 1; i <= n + 1; ++ i) {
LL minf = kInf;
for (int j = 1; j <= n + 1; ++ j) {
if (i == j) continue;
minf = std::min(minf, f[(all - 1) ^ (1 << (i - 1))][j]);
}
sum[i][lst] += minf;
}
}
all = (1 << (m + 1));
for (int lst = 1; lst <= n + 1; ++ lst) {
for (int s = 1; s < all; ++ s) {
for (int i = 1; i <= m + 1; ++ i) {
f[s][i] = kInf;
}
}
for (int i = 1; i <= m + 1; ++ i) f[1 << (i - 1)][i] = 0;
for (int s = 1; s < all; ++ s) {
for (int i = 1; i <= m + 1; ++ i) {
if ((s >> (i - 1) & 1) == 0) continue;
for (int j = 1; j <= m + 1; ++ j) {
if (i == j || (s >> (j - 1) & 1)) continue;
f[s | (1 << (j - 1))][j] = std::min(f[s | (1 << (j - 1))][j],
f[s][i] + col[i][j] - abs(a[lst][i] - a[lst][j]));
}
}
}
for (int i = 1; i <= m + 1; ++ i) {
LL minf = kInf;
for (int j = 1; j <= m + 1; ++ j) {
if (i == j) continue;
minf = std::min(minf, f[(all - 1) ^ (1 << (i - 1))][j]);
}
sum[lst][i] += minf;
}
}
ans = kInf;
for (int i = 1; i <= n + 1; ++ i) {
for (int j = 1; j <= m + 1; ++ j) {
ans = std::min(ans, sum[i][j]);
}
}
}
//=============================================================
int main() {
// freopen("1.txt", "r", stdin);
std::ios::sync_with_stdio(0), std::cin.tie(0);
int T; std::cin >> T;
while (T --) {
std::cin >> n >> m;
for (int i = 1; i <= n; ++ i) {
for (int j = 1; j <= m; ++ j) {
std::cin >> a[i][j];
}
}
init();
DP();
std::cout << ans << "\n";
}
return 0;
}
/*
1
1 2
1 3
*/
标签:le,int,最小值,异或,continue,Grid,一维,CF1993E,Problem
From: https://www.cnblogs.com/luckyblock/p/18399633