解いた問題のソースコードと解説など。


SRM 549 Div2 Hard OrderOfTheHats

問題

http://apps.topcoder.com/wiki/display/tc/SRM+549

あるグラフの隣接行列が与えられる。このグラフに辺を加えるか、すでにある辺を取り除くかしてacyclicにしたい。必要な手順の最小回数を求めよ。

やりかた

Editorialを参考にしました。

bitDP。
acyclicにするという目的上、辺を新たに加えるという操作をする必要はない。とすると、するべき操作は辺を取り除くだけになる。acyclicであればグラフはトポロジカルにソート可能であって、逆もまた然り。そのため頂点がある順列でトポロジカルにソートされているとしたときに、トポロジ順とは逆向きに張っている辺を取り除けばいい。この辺の数がこの順列にたいしての手順数になる。よってすべての順列について試し、この内最小のものを探せばいい。
実際はどうやるかということについては頂点数が20以下であることから、bitDPで解くことができる。
bitDP[mask] := (頂点集合maskに対して、グラフをacyclicにするための最小手順数)

このように定義すると、次に頂点iを追加するとき
dp[mask]は、「maskに頂点iを加えた集合」に対して頂点iから伸びている辺の数(つまり逆辺の数)+dp[mask | (1 << i)]

という形で更新していくことができる。
以下ソース。

int n, adj[22];
int dp[1 << 20];

class OrderOfTheHats {
public:
  int F(int mask){
    if(mask == (1 << n) - 1) return 0;
    
    int &ret = dp[mask];
    if(ret > -1) return ret;
    ret = INF;
    for(int i = 0; i < n; i++){
      if(!(mask >> i & 1)){
	int next = mask | (1 << i);
	ret = min(ret, __builtin_popcount(next & adj[i]) + F(mask | (1 << i)));
      }
    }
    return ret;
  }
  int minChanged(vector <string> spellChart) {
    n = spellChart.size();
    for(int i = 0; i < n; i++){
      adj[i] = 0;
      for(int j = 0; j < n; j++)
	if(spellChart[i][j] == 'Y')
	  adj[i] |= (1 << j);
    }
    memset(dp, -1, sizeof(dp));
    return F(0);
  }
};

良い問題だった。まだすんなりとできる問題ではない。

11月はサボりすぎた。本来は9月いっぱいで554までやるつもりが2ヶ月もオーバーしてしまった。見積りが甘いのもあったが、11月は想像より忙しく時間も取れなかった。レートも少し下がったし。むむ。

しょげないでよBaby 眠れば治る