の計算量で良いなら簡単。
「どこに値 の要素があるのか」を管理するというテクニックをここで習得しよう!
問題概要
の並び替えである順列
が与えられる。これをソートしたい。以下の操作を
回まで実施できる。
を選んで、
と
を swap する
ソートするための操作列を 1 つ求めよ (必ず可能であることは示せる)。
制約
考えたこと
この手の問題では、最初は計算量を気にせずに考えるとよい。最後に高速化を試みよう。
この問題に対しては、トランプなどでカードが配られて、それを見やすい順に並び替えるときのことを思い出すと、次のような解法が自然に思い浮かぶ。
- まず、1 を探して、それを左から 1 番目に持ってくる
- ただし、もともと左から 1 番目が 1 のときは何もしない
- 次に、2 を探して、それを左から 2 番目に持ってくる
- ただし、もともと左から 2 番目が 2 のときは何もしない
- ...
- 最後に、
を探して、それを左から
番目に持ってくる
- ただし、もともと左から
番目が
のときは何もしない
- ただし、もともと左から
最後を ではなく、
までにしているのは、「左から
までが揃った時点で、
の位置も自動的に揃っているため」だ。
さて、値 について考えているとき、愚直にやると、以下の処理に
の計算量を要する。
の中から、値が
であるものを探す
そのため、全体の計算量は となってしまう。上記の処理を高速化しよう。
高速化
上記の処理を高速化するためには、次の情報を管理すればよい!! この「どこにその要素があるのかを管理するテク」はより高難易度の問題では頻出する!!
where[v]←であるような
この値を管理すれば、上記の「 の中から、値が
であるものを探す」という処理を
の計算量でこなすことができて、全体の計算量も
となるのである。
where の更新
最後に、 と
を swap するときに、この配列
where も更新する必要があることに注意しよう。慣れないと難しいかもしれない。次のようにできる (これは憶えてしまおう)。
swap(where[A[i]], where[A[j]]); swap(A[i], A[j]);
コード
全体的に 0-indexed で実装した。
#include <bits/stdc++.h> using namespace std; using pint = pair<int, int>; int main() { int N; cin >> N; vector<int> A(N), where(N); for (int i = 0; i < N; ++i) { cin >> A[i]; --A[i]; // 値 A[i] があるのは i 番目 where[A[i]] = i; } // 左から i 番目に、値 i を持ってくる処理をする vector<pint> res; for (int i = 0; i <= N - 2; ++i) { // i 番目がすでに値 i なら何もしない if (A[i] == i) continue; // 値 i がある場所 int j = where[i]; // A[i] と A[j] を swap する swap(where[A[i]], where[A[j]]); swap(A[i], A[j]); res.emplace_back(i, j); } // 出力 cout << res.size() << endl; for (auto [i, j] : res) { cout << i+1 << " " << j+1 << endl; } }