GaleSharpley アルゴリズムの場合とは異なり, 戦略を取る主体が $1$ グループの場合の安定マッチングを求めるアルゴリズムである.
言い方を変えると GaleSharpley のアルゴリズムは完全二部グラフ上の安定マッチングを求めていたのに対しこのアルゴリズムは完全グラフ上の安定マッチングを求めるアルゴリズムとなっている.
安定ルームメイト問題とも呼ばれ、 $n$ 人($n$ は偶数とする) のグループがいたときに各メンバーが他の $n-1$ 人に対する優先順位のリストを持っていて誰と誰が roommate になるかをマッチングするというような状況である.
この場合の blocking pair も安定結婚問題と同様にあるペアでそれぞれのマッチング相手が今見ているペアの相手よりも優先順位より低い場合のことをいい、 そのような blocking pair の存在しないマッチングを安定マッチングと呼ぶ.
この問題において, 安定マッチングは常に存在するとは限らず Irving のアルゴリズムは安定マッチングが存在する場合にのみその $1$ つを返し, そうでない場合は存在しないと判定する.
元論文は "An Efficient Algorithm for the “Stable Roommates” Problem" [Irving 1985]"
(関数)
solve$(\_ pref)$ : 優先度の高い順に相手の ID を格納した行列($n \times (n-1)$ で $n$ は偶数) を渡す(存在しない場合は要素数 $0$ の vector を返す).
時間計算量: $\O (n^2)$
- class Irving
- {
- private:
- int n;
- vector<list<int> > pref;
- vector<vector<list<int>::iterator> > pref_iter;
- vector<vector<int> > order;
- list<int> pref_index;
- vector<list<int>::iterator> pref_index_iter;
- bool _erase(int num1, int num2)
- {
- pref[num1].erase(pref_iter[num1][order[num1][num2]]);
- pref[num2].erase(pref_iter[num2][order[num2][num1]]);
- return pref[num1].empty() || pref[num2].empty();
- }
- bool __erase(int num1, int num2)
- {
- pref[num1].erase(pref_iter[num1][order[num1][num2]]);
- pref[num2].erase(pref_iter[num2][order[num2][num1]]);
- if(pref[num1].size() == 1) pref_index.erase(pref_index_iter[num1]);
- if(pref[num2].size() == 1) pref_index.erase(pref_index_iter[num2]);
- return pref[num1].empty() || pref[num2].empty();
- }
- bool reduction(const vector<int>& cand)
- {
- pref_index_iter.resize(n);
- for(int i = 0; i < n; ++i){
- if(cand[i] < 0) continue;
- for(auto it = --pref[i].end(); *it != cand[i]; it = --pref[i].end()){
- if(_erase(i, *it)) return true;
- }
- if(pref[i].size() > 1){
- pref_index.push_back(i);
- pref_index_iter[i] = --pref_index.end();
- }
- }
- return false;
- }
- bool phase1()
- {
- stack<int> single;
- for(int i = 0; i < n; ++i){
- single.push(i);
- }
- vector<int> cand(n, -1);
- while(!single.empty()){
- int u = single.top();
- single.pop();
- while(true){
- int v = *pref[u].begin();
- if(cand[v] < 0){
- cand[v] = u;
- break;
- }else if(order[v][cand[v]] > order[v][u]){
- single.push(cand[v]);
- if(_erase(v, cand[v])) return true;
- cand[v] = u;
- break;
- }else{
- if(_erase(v, u)) return true;
- }
- }
- }
- return reduction(cand);
- }
- int eliminate(unique_ptr<bool[]>&& visit, int cur)
- {
- list<int> pseq;
- while(!visit[cur]){
- pseq.push_back(cur);
- visit[cur] = true;
- cur = *(--pref[*(++pref[cur].begin())].end());
- }
- list<int>::iterator it = pseq.begin();
- for(;;++it){
- visit[*it] = false;
- if(*it == cur) break;
- }
- list<pair<int, int> > elim;
- for(;it != pseq.end(); ++it){
- visit[*it] = false;
- elim.emplace_back(*it, *(++pref[*it].begin()));
- }
- for(pair<int, int>& value : elim){
- int p = value.first, q = value.second;
- if(*pref[p].begin() != q){
- if(__erase(p, *pref[p].begin())) return -1;
- }
- for(auto it = --pref[q].end(); *it != p; it = --pref[q].end()){
- if(__erase(q, *it)) return -1;
- }
- }
- return cur;
- }
- bool phase2()
- {
- if(pref_index.empty()) return false;
- unique_ptr<bool[]> visit(new bool[n]());
- int start = *pref_index.begin();
- do {
- if(pref[start].size() == 1) start = *pref_index.begin();
- if((start = eliminate(move(visit), start)) < 0) return true;
- }while(!pref_index.empty());
- return false;
- }
- public:
- Irving(){}
- vector<int> solve(const vector<vector<int> >& _pref)
- {
- n = (int)_pref.size();
- assert(n % 2 == 0);
- pref.resize(n);
- pref_iter.resize(n, vector<list<int>::iterator>(n-1));
- order.resize(n, vector<int>(n));
- for(int i = 0; i < n; ++i){
- for(int j = 0; j < n-1; ++j){
- pref[i].push_back(_pref[i][j]);
- pref_iter[i][j] = --pref[i].end();
- order[i][_pref[i][j]] = j;
- }
- }
- if(phase1()) return vector<int>();
- if(phase2()) return vector<int>();
- vector<int> ans(n);
- for(int i = 0; i < n; ++i){
- ans[i] = *pref[i].begin();
- }
- return ans;
- }
- };
verify していません(verify 問題を知らない)