CF1993E Xor-Grid Problem

结论,异或,状压 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
*/
posted @ 2024-09-06 09:38  Luckyblock  阅读(15)  评论(0编辑  收藏  举报