[CF1713E] Cross Swapping

题目简介

给一个 \(n \times n\) 的矩阵,可以进行任意次操作。

每次操作可以任选一个数字 \(k \le n\) ,交换第 \(k\) 行与第 \(k\) 列,即:

// choose the number k
for(int i = 1; i <= n; ++i)
  swap(a[i][k], a[k][i]);

要求最后矩阵的字典序最小,求字典序最小的矩阵。

题目中矩阵的字典序定义为以 \(a_{(i-1) \times n + j}\) 为序(从上往下每一行,从左往右),像比较字符串那样一个一个比较。

正解

考虑选择了 \(k\) 对某一个单独的位置 \(a_{i, j}\) 产生的影响。

如果 \(k\) 等于 \(i\)\(j\),那么 \(a_{i, j}\)\(a_{j, i}\) 交换,再选择一次 \(k\) 则影响消除(恢复原来的样子)。

那么其实 \(a_{i, j}\) 最多出现在两个位置,而且每一个 \(k\) 最多选择一次就够了。

按字典序影响从大到小来考虑,枚举右上角的上三角矩阵(因为位置对称的可以一起考虑到)。

如果 \(a_{i, j} < a_{j, i}\),那么 \(i\)\(j\) 要么同时选,要么同时不选。

如果 \(a_{i, j} > a_{j, i}\),那么 \(i\)\(j\) 选择的情况是相反的(一个 0 一个 1)。

如果 \(a_{i, j} = a_{j, i}\),其实对 \(i\)\(j\) 之间选择的关系没有影响,无所谓的。

这个东西选和不选可以用并查集巧妙的维护,而且并查集还可以做到优先满足前面的约束。

如果 \(x\)\(y\) 属于同一个集合,令 fa[find(x)] = find(y)。

否则令 fa[find(x)] = -find(y)。

但是要注意一下正负号,并查集的代码就按下面这样写:

int fa[N];
int find(int u) {
    if(u < 0) return -find(-u);
    return fa[u] == u ? u : fa[u] = find(fa[u]);
}
void merge(int u, int v) {
    u = find(u), v = find(v);
    if(abs(u) != abs(v)) {
        if(u > 0) fa[u] = v;
        else fa[-u] = -v;
    }
}

代码

#include <bits/stdc++.h>

using namespace std;

const int N = 1005;
int a[N][N], fa[N];

int find(int u) {
    if(u < 0) return -find(-u);
    return fa[u] == u ? u : fa[u] = find(fa[u]);
}
void merge(int u, int v) {
    u = find(u), v = find(v);
    if(abs(u) != abs(v)) {
        if(u > 0) fa[u] = v;
        else fa[-u] = -v;
    }
}

void solve() {
    int n;
    cin >> n;
    for(int i = 1; i <= n; ++i) {
        fa[i] = i;
        for(int j = 1; j <= n; ++j)
            cin >> a[i][j];
    }
    for(int i = 1; i <= n; ++i)
        for(int j = 1; j <= n; ++j) {
            if(a[i][j] < a[j][i]) merge(i, j);
            if(a[i][j] > a[j][i]) merge(i, -j);
        }
    for(int i = 1; i <= n; ++i) {
        if(find(i) < 0) continue;
        for(int k = 1; k <= n; ++k)
            swap(a[i][k], a[k][i]);
    }
    for(int i = 1; i <= n; ++i) {
        for(int j = 1; j <= n; ++j)
            cout << a[i][j] << ' ';
        cout << '\n';
    }
}

int main() {
    ios::sync_with_stdio(false);
    int T;
    cin >> T;
    while(T--) solve();
    system("pause");
    return 0;
}
posted @ 2022-08-07 12:02  Lskkkno1  阅读(138)  评论(0编辑  收藏  举报