はまやんはまやんはまやん

hamayanhamayan's blog

Flipping Matrix [CSAcademy #66 D]

https://csacademy.com/contest/round-66/task/flipping-matrix/

N×Nのバイナリ行列がある。
以下のクエリを行う。
「R x y」x行目とy行目をスワップする
「C x y」x列目とy列目をスワップする
N回以下、このクエリを行って、主対角線全てを1にできるか判定せよ。
出来るならクエリも示せ。

解法

まずクエリを最小回数に抑える必要が無いというのが重要な所。
あとは、クエリは2種類あるが1種類で実現可能であるということ。
二番目は根拠が無いのだが、経験的によくある。
 
まず実現可能かであるが、これは二部マッチングでできる。
もし実現可能であれば、スワップを構築するだけだが、移動先に遷移を貼るとすると、これはN頂点N辺のグラフとなるので、連結成分毎にまとめて、上手くスワップを作ればいい。

int N, X[1010][1010];
//---------------------------------------------------------------------------------------------------
void _main() {
    cin >> N;
    rep(y, 0, N) rep(x, 0, N) cin >> X[y][x];

    BipartiteMatching bm(N, N);
    rep(y, 0, N) {
        rep(x, 0, N) if (X[y][x] == 1) bm.add_edge(y, x);
    }

    if (bm.match() != N) {
        printf("-1\n");
        return;
    }

    UnionFind uf(N);
    rep(i, 0, N) uf(i, bm.whois(i));
    map<int, vector<int>> cmp;
    rep(i, 0, N) cmp[uf[i]].push_back(i);
    
    fore(p, cmp) {
        auto &v = p.second;
        if (v.size() == 1) continue;

        int st = v[0], cur = v[0];
        while (bm.whois(cur) != st) {
            int a = cur;
            int b = bm.whois(cur);
            printf("C %d %d\n", a + 1, b + 1);
            cur = b;
        }
    }
}