SRM 297 DIV1 Hard DynamiteBoxes
問題
箱を2*2*heightに積む。
それぞれの箱は独立に、0.5の確率でダイナマイトが入っていて、そうでなければ空である。
「ダイナマイトクラスター」とはダイナマイトの入った箱の集合で、連結なものである(面でのみ接しているとみなす)。
サイズがdangerousClusterSize以上であるダイナマイトクラスターを含む確率を求めよ
- 1 <= height <= 30
- 1 <= dangerousClusterSize <= 121
解答
DP。まず、2*2なので、各段ごとに最大でも2つの独立のクラスターしか関係しないことに注意する。
なので(高さ * 前の段の状態bitmask * 前の段のクラスターサイズ1 * 前の段のクラスターサイズ2)というふうな状態でできる。
さて、この状態の漸化式を書かなければいけない。
無理やりサイズの状態を2つにしたことによってそれが複雑になってしまう。
自分は(前のmask,今のmask)ごとの((サイズ2つ,1)→サイズ2つ)の行列を前計算(それが複雑に!)したが、実際には上手く場合分けするのもいいのではないかと思う。
あるいは、rng_58さんのコードのように、存在フラグとconnectサイズを4つ全部に持つ状態で、それをmapでやれば簡単で、これが最良なのではないかと思う
コメント
コード
割と汚く長い
#include <vector> #include <cstring> #define rep(i,n) for(int (i)=0;(i)<(int)(n);++(i)) #define rer(i,l,u) for(int (i)=(int)(l);(i)<=(int)(u);++(i)) #define reu(i,l,u) for(int (i)=(int)(l);(i)<(int)(u);++(i)) #define mset(m,v) memset(m,v,sizeof(m)) using namespace std; struct UnionFind { vector<int> data; UnionFind(int size_) : data(size_, -1) { } bool unionSet(int x, int y) { x = root(x); y = root(y); if (x != y) { if (data[y] < data[x]) swap(x, y); data[x] += data[y]; data[y] = x; } return x != y; } bool findSet(int x, int y) { return root(x) == root(y); } int root(int x) { return data[x] < 0 ? x : data[x] = root(data[x]); } int size(int x) { return -data[root(x)]; } }; struct DynamiteBoxes { double getProbability(int height, int dangerousClusterSize) { static int nsizetable[1<<4][1<<4][2][6]; rep(mask, 1<<4) rep(nmask, 1<<4) rep(connected, 2) { mset(nsizetable[mask][nmask][connected], 0); int *mat = nsizetable[mask][nmask][connected]; UnionFind uf(8), uf2(4); rep(i, 4) if(nmask >> i & 1) rep(j, 4) if(i != j && (nmask >> j & 1) && j != 3 - i) uf.unionSet(i, j); rep(i, 4) if(mask >> i & 1) rep(j, 4) if(i != j && (mask >> j & 1) && (connected || j != 3 - i)) uf.unionSet(4+i, 4+j), uf2.unionSet(i, j); rep(i, 4) if((nmask >> i & 1) && (mask >> i & 1)) uf.unionSet(i, 4+i); rep(i, 4) if(nmask >> i & 1) reu(j, i+1, 4) if(nmask >> j & 1) if(!uf.findSet(i, j)) { //新たなものはconnectedでない rep(k, 4) if(uf.findSet(i, k)) mat[2] ++; rep(k, 4) if(uf.findSet(j, k)) mat[5] ++; rep(i2, 4) if(mask >> i2 & 1) reu(j2, i2+1, 4) if(mask >> j2 & 1) if(!uf2.findSet(i2, j2)) { if(uf.findSet(i, 4+i2)) mat[0] = 1; if(uf.findSet(j, 4+j2)) mat[4] = 1; goto next; } rep(i2, 4) if(uf.findSet(i, 4+i2)) mat[0] = 1; rep(i2, 4) if(uf.findSet(j, 4+i2)) mat[3] = 1; goto next; } //新たなものはconnected rep(i, 4) if(nmask >> i & 1) mat[2] ++; rep(i, 4) if(nmask >> i & 1) { rep(i2, 4) if(mask >> i2 & 1) reu(j2, i2+1, 4) if(mask >> j2 & 1) if(!uf2.findSet(i2, j2)) { if(uf.findSet(i, 4+i2)) mat[0] = 1; if(uf.findSet(i, 4+j2)) mat[1] = 1; goto next; } rep(i2, 4) if(mask >> i2 & 1) if(uf.findSet(4+i2, i)) mat[0] = 1; } next:; } //高さ, 前の状態, 繋がりサイズ1, 繋がりサイズ2 static double dp[31][1<<4][122][122]; mset(dp, 0); dp[0][0][0][0] = 1; //size1: top left, size2: bottom right rep(h, height) rep(mask, 1 << 4) rer(size1, 0, dangerousClusterSize) rer(size2, 0, dangerousClusterSize) if(double tmp = dp[h][mask][size1][size2]) { tmp /= 16; if(size1 == dangerousClusterSize || size2 == dangerousClusterSize) { rep(nmask, 1<<4) dp[h+1][nmask][dangerousClusterSize][dangerousClusterSize] += tmp; }else { rep(nmask, 1<<4) { int *mat = nsizetable[mask][nmask][size2==0]; dp[h+1][nmask] [min(dangerousClusterSize, mat[0]*size1+mat[1]*size2+mat[2])] [min(dangerousClusterSize, mat[3]*size1+mat[4]*size2+mat[5])] += tmp; } } } double r = 0; for(int mask = 0; mask < 1<<4; mask ++) for(int size1 = 0; size1 <= dangerousClusterSize; size1 ++) for(int size2 = 0; size2 <= dangerousClusterSize; size2 ++) { if(size1 == dangerousClusterSize || size2 == dangerousClusterSize) r += dp[height][mask][size1][size2]; } return r; } };