今回は惨敗したけど次回また頑張りたい!!!
問題概要
の順列であって、以下の条件を満たすもののうち、辞書順最小のものを求めよ。存在しない場合は -1 を出力せよ。
- の右隣の値は ではない
制約
考えたこと
色々手を動かしてみると、条件を満たすものが存在しないことは、そうそうない気がしてくる。1 個 1 個の条件はゆるくて、 通りすべての可能性を潰すことなどできなさそうである。 の場合だけ、存在しないことがありうる (サンプルにある)。
そして、ほとんどの場合、
- まだ使っていないもののうち、現在の最後尾の右に来れるやつのうち、最小のものを選ぶ
という感じの Greedy で良さそうに思える。しかしこれには例外があって、たとえば として とかの場合、先頭に 7 以外を置いてしまうと詰む。
修正として、残り 3 個になるまでは
を貪欲に選んだ要素とする。ここで を採用してしまうと、残ってるものがすべて を嫌っている状態になったら、 の代わりに を採用する
という風にすれば OK。 を選んでしまうと を置けなくなってしまうのだが、その瞬間に を選べば大丈夫。ただし最後の 3 個あたりは微妙なので最後の方は全探索する。ちゃんとした証明は editorial にあって、厳密には、以下のことを残りの要素数に関する帰納法で示せるっぽい。
要素 を選んだときに、残りの要素が 以上とする。このとき、残りの要素の中に を嫌っていないものが存在するならば、残りの要素で条件を満たすようにできる
迷走したこと
以下のことを心配してしまった。実際はそんなことは起こらない。 を採用したときに、新たに「詰み」を増やしてしまう可能性があるのは のみ。
D、貪欲していったときに同時に複数人が「嫌われすぎて先頭にしか置けない」という状態になったらどうしよう...となってた。
— けんちょん (@drken1215) 2020年1月11日
#include <iostream> #include <sstream> #include <cstdio> #include <cstdlib> #include <cmath> #include <ctime> #include <cstring> #include <string> #include <vector> #include <stack> #include <queue> #include <deque> #include <map> #include <set> #include <bitset> #include <numeric> #include <utility> #include <iomanip> #include <algorithm> #include <functional> #include <unordered_map> using namespace std; int N; vector<int> p; vector<set<int>> ip; bool check(const vector<int> &res) { for (int i = 0; i + 1 < res.size(); ++i) { if (res[i+1] == p[res[i]]) return false; } return true; } void last(vector<int> &res, vector<int> &rem) { do { if (res.size() > 0 && rem[0] == p[res.back()]) continue; if (check(rem)) { for (int i = 0; i < res.size(); ++i) cout << res[i] + 1 << " "; for (int i = 0; i < rem.size(); ++i) cout << rem[i] + 1 << " "; cout << endl; return; } } while (next_permutation(rem.begin(), rem.end())); cout << -1 << endl; return; } const int NOKORI = 5; void solve() { if (N <= NOKORI) { vector<int> res, rem(N); for (int i = 0; i < N; ++i) rem[i] = i; last(res, rem); return; } vector<int> res; set<int> rem; for (int i = 0; i < N; ++i) rem.insert(i); while (N - (int)res.size() > NOKORI) { int dame = -1; if (res.size() > 0) dame = p[res.back()]; // とりあえず Greedy に int ne = -1; for (auto it : rem) { if (it == dame) continue; ne = it; break; } // ne を選んでしまうと詰むときは取り替える if (ip[p[ne]].size() + res.size() == N-1) { ne = p[ne]; } // ne を選ぶ res.push_back(ne); rem.erase(ne); ip[p[ne]].erase(ne); } vector<int> vrem; for (int i = 0; i < N; ++i) if (rem.count(i)) vrem.push_back(i); last(res, vrem); } int main() { while (cin >> N) { p.resize(N); ip.assign(N, set<int>()); for (int i = 0; i < N; ++i) { cin >> p[i]; --p[i]; ip[p[i]].insert(i); } solve(); } }