実行時間制限: 2 sec / メモリ制限: 1024 MB / Difficulty: 394
問題概要
数列 を並び替えてできる数列 が与えられる。次の操作を0回以上 回以下行うことで、 を昇順にソートしたい。操作回数を として、1行目には を、続く 行には各操作で選ぶ整数 を空白区切りで出力せよ。
- 操作 : を満たす整数の組 を選び、 と を入れ替える。
制約
- 入力はすべて整数。
考察
一見するとバブルソートもしくは選択ソートの手順に見えるが、それらの計算量は であり今回の制約では TLE する。典型のソートアルゴリズムはいったん忘れて、純粋に考えてみよう。
最大操作回数が 回であることに注目すると、もっとも思いつきやすいのは次のアルゴリズムだろう。
- の先頭から が正しい位置にあるか (先頭から 番目にあるか、つまり であるか) どうかを判定していく。
- もし正しい位置にない場合、 となるような を求める。
- と の位置を入れ替える。
しかし、2の過程で最悪 回の探索が必要となり、 TLE となってしまう (例えば、 のとき) 。
これを改善するために、あらかじめ となるような を格納する配列 (ここではpos
とした) を作成しておき、2, 3の過程をそれぞれ次のように書き換えてみよう。
2'. もし正しい位置にない場合、 とする。
3'. と の位置、 と の位置をそれぞれ入れ替える。
こうすることでこの部分の処理は定数時間で行うことができるようになり、全体として計算量を に落とすことができる。
あとはこれを実装していく。各操作で選んだ の組を出力する必要があるので、処理のしやすさからpair
型のvector
配列にその組を格納している。
コード
#include <bits/stdc++.h> using namespace std; #define rep(i, start, end) for (auto i = (start); (i) < (end); (i)++) using Pair_int = pair<int, int>; // ======================================== // int main() { int N; cin >> N; vector<int> A(N + 1), pos(N + 1); rep(i, 1, N + 1) { int a; cin >> a; A[i] = a; pos[a] = i; } vector<Pair_int> ans; rep(i, 1, N + 1) { if (A[i] != i) { int j = pos[i]; swap(A[i], A[j]); swap(pos[A[i]], pos[A[j]]); ans.push_back(make_pair(i, j)); } } cout << ans.size() << endl; for (auto p : ans) { cout << p.first << " " << p.second << endl; } }
実装時間: 25分
これが灰Diffの時代になってしまったのか...