phyllo’s algorithm note

レッドコーダーへの道のりは遠い。休んでる場合じゃない!

yukicoder No.519 アイドルユニット

問題

N人(2の倍数)のアイドルがいる。
全員ペアを組ませて売り出したい。
しかし、ペアには相性度が決まっており、すべてのペアの相性度の合計値が最大になるようにしたい。
相性度の合計の最大値を返せ。

制約

1 <= N <= 24
0 <= 各ペアの相性度 <= 1000

解説

一般的には、グラフの重み付きマッチングの問題らしい。
ここでは、各アイドルがペアを組んでいるかどうかを状態として持つbitDPを考える。

単純に考えると、「各状態でペアを組んでいないアイドル2人を選んで、状態を更新」するように計算する。

int N;
int cost[30][30];

int dp[1<<25];

int main(){
  cin >> N;
  rep(i,N){
    rep(j,N){
      cin >> cost[i][j];
    }
  }

  rep(i,1<<25) dp[i] = -1;
  dp[0] = 0;

  rep(i,1<<N){
    if(dp[i] < 0) continue;
    rep(j,N) if(((i>>j)&1) == 0){
      REP(k,j+1,N) if(((i>>k)&1) == 0){
        int ii = i | (1<<j) | (1<<k);
        dp[ii] = max(dp[ii], cost[j][k] + dp[i]);
      }
    }
  }
  cout << dp[(1<<N)-1] << endl;
  return 0;
}

これだとO(2^N * N^2)でTLEしてしまう。


実は上記のループは無駄なところがある。
状態が「100100」の場合を考える。
(この状態でjとkのループでは、「1**100」「1*01*0」「1*010*」「10*1*0」「10*10*」「1001**」を列挙して状態を更新している。)
ここで、
「100100」から「110110」と遷移して「111111」に至るケースと、
「100100」から「101101」と遷移して「111111」に至るケースでは、
同じペアの組み方なのに、2回計算してしまっている。

ペアの推移的には、
(0,3)->(0,3)(1,4)->(0,3)(1,4)(2,5)
(0,3)->(0,3)(2,5)->(0,3)(1,4)(2,5)
のような感じ。

このような場合、同じペアの組み方をするのは1通りだけ考えればよいので、状態の中でまだペアを組んでいないアイドル(状態が0のところ)を一人選んで、そのアイドルを必ず取る場合を考えて、そのペア候補だけ(kのループ)を考えればよい。

  rep(i,1<<N){
    if(dp[i] < 0) continue;
    rep(j,N) if(((i>>j)&1) == 0){
      REP(k,j+1,N) if(((i>>k)&1) == 0){
        int ii = i | (1<<j) | (1<<k);
        dp[ii] = max(dp[ii], cost[j][k] + dp[i]);
      }
      break; //これを入れるだけ
    }
  }

計算量は、O(2^N * N)になり、間に合う。