ferin blog

Sep 13, 2018 - 3 minute read - 競技プログラミング

CSA #66 D - Flipping Matrix

問題ページ

ある場所の1をswap操作を用いて対角線上にもっていったとする。するとこの1と同じ行,列に存在する1を対角線上にswapで持っていくことは不可能である。したがって対角線上に1を並べるには同じ行、列に存在しないという条件下でN個の1を選ぶ必要がある。
2N個の頂点を使ってN個を行、もうN個は列としたグラフを考える。1が(y,x)に存在するのであれば行yを表す頂点と列xを表す頂点をつなぐ辺を張る。そしてこのグラフ上で最大マッチングを求める。このグラフは二部マッチングなので最大フローを用いて最大マッチングを求めることができる。
流量が1の辺を見ることで(最大マッチングに使用されている辺)=(対角線上に置かれる1)を求めることができる。使用する1の位置を求めればあとは対角線上に置かれるように1をswapしていけばよい。v[i]=(i行目で使用する1が何列目にあるか) と記録しておくとi列目とv[i]列目をswapする操作を出力するを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
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include <bits/stdc++.h>

using namespace std;
using ll = long long;
#define int ll
using PII = pair<int, int>;
template <typename T> using V = vector<T>;
template <typename T> using VV = vector<V<T>>;
template <typename T> using VVV = vector<VV<T>>;

#define FOR(i, a, n) for (ll i = (ll)a; i < (ll)n; ++i)
#define REP(i, n) FOR(i, 0, n)
#define ALL(x) x.begin(), x.end()
#define PB push_back

const ll INF = (1LL<<60);
const int MOD = 1000000007;

template <typename T> T &chmin(T &a, const T &b) { return a = min(a, b); }
template <typename T> T &chmax(T &a, const T &b) { return a = max(a, b); }
template <typename T> bool IN(T a, T b, T x) { return a<=x&&x<b; }
template<typename T> T ceil(T a, T b) { return a/b + !!(a%b); }
template<class S,class T>
ostream &operator <<(ostream& out,const pair<S,T>& a){
  out<<'('<<a.first<<','<<a.second<<')';
  return out;
}
template<class T>
ostream &operator <<(ostream& out,const vector<T>& a){
  out<<'[';
  REP(i, a.size()) {out<<a[i];if(i!=a.size()-1)out<<',';}
  out<<']';
  return out;
}

int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};

struct foldFulkerson {
  struct edge {int to, cap, rev;};
  int n;
  VV<edge> g;
  V<int> used;

  foldFulkerson(){}
  foldFulkerson(int n_) : n(n_) {
    g.assign(n, V<edge>());
  }

  void add_edge(int from, int to, int cap) {
    g[from].push_back({to, cap, (int)g[to].size()});
    g[to].push_back({from, 0, (int)g[from].size()-1});
  }

  int dfs(int v, int t, int f) {
    if(v == t) return f;
    used[v] = 1;
    for(auto &e: g[v]) {
      if(!used[e.to] && e.cap > 0) {
        int d = dfs(e.to, t, min(f, e.cap));
        if(d > 0) {
          e.cap -= d;
          g[e.to][e.rev].cap += d;
          return d;
        }
      }
    }
    return 0;
  }

  int max_flow(int s, int t) {
    int flow = 0;
    while(true) {
      used.assign(n, 0);
      int f = dfs(s, t, INF);
      if(f == 0) return flow;
      flow += f;
    }
  }
};

signed main(void)
{
  cin.tie(0);
  ios::sync_with_stdio(false);

  int n;
  cin >> n;
  VV<int> a(n, V<int>(n));
  REP(i, n) REP(j, n) cin >> a[i][j];

  foldFulkerson flow(2*n+2);
  REP(i, n) REP(j, n) {
    if(a[i][j] == 1) flow.add_edge(i, n+j, 1);
  }
  REP(i, n) {
    flow.add_edge(2*n, i, 1);
    flow.add_edge(n+i, 2*n+1, 1);
  }

  int ret = flow.max_flow(2*n, 2*n+1);
  if(ret != n) {
    cout << -1 << endl;
    return 0;
  }

  // v[i] = (i行目で使う1が何列目にあるか)
  V<int> v(n);
  REP(i, n) {
    for(auto j: flow.g[i]) {
      if(j.cap == 0) {
        v[i] = j.to - n;
      }
    }
  }


  REP(i, n) {
    // v[i] を i に移す
    if(i == v[i]) continue;
    cout << "C " << i+1 << " " << v[i]+1 << endl;
    int idx1, idx2;
    REP(j, n) {
      if(v[j] == i) idx1 = j;
      if(v[j] == v[i]) idx2 = j;
    }
    swap(v[idx1], v[idx2]);
  }

  return 0;
}