Topcoder RandomPaintingOnABoard

RandomPaintingOnABoard

\(n\times m\) 的棋盘,每个位置有 \(p_{i,j}\)。每轮 \((i,j)\) 被选中的概率为 \(\frac{p_{i,j}}{sum}\)

问⾄少⼏轮后每⼀⾏⼀列⾄少⼀个被选中。

\(nm\leq 150,\max\{n,m\}\leq 21,0\leq p_{i,j}\leq 9\)

题解

显然是min-max容斥。如何DP呢?

注意到 \(nm\leq 150\),那么 \(\min\{n,m\}\leq 12\)。假设行数更小,那么我们可以暴力枚举行的子集,对列做DP。

\(dp(i,j,k)\) 表示前 \(i\) 列,\(\sum p=j\),奇偶性为 \(k\) 的方案数。1行0列的时候贡献系数应为正。

时间复杂度 \(O(2^n m~sum~n)\)

int a[21][21];
LD f[2][1351];

class RandomPaintingOnABoard{
public:
    LD expectedSteps(CO vector<string>&prob){
        int n=prob.size(),m=prob[0].size();
        if(n<=m){
            for(int i=0;i<n;++i)for(int j=0;j<m;++j)
                a[i][j]=prob[i][j]-'0';
        }
        else{
            for(int i=0;i<n;++i)for(int j=0;j<m;++j)
                a[j][i]=prob[i][j]-'0';
            swap(n,m);
        }
        int tot=0;
        for(int i=0;i<n;++i)for(int j=0;j<m;++j)
            tot+=a[i][j];
        LD ans=0;
        for(int s=0;s<1<<n;++s){
            int cur=0,sum=0;
            for(int i=0;i<n;++i)if(s>>i&1){
                cur^=1;
                for(int j=0;j<m;++j) sum+=a[i][j];
            }
            memset(f,0,sizeof f),f[cur][sum]=1;
            for(int j=0;j<m;++j){
                int sum=0;
                for(int i=0;i<n;++i)if(~s>>i&1)
                    sum+=a[i][j];
                for(int i=tot;i>=sum;--i){
                    LD x=f[0][i-sum],y=f[1][i-sum];
                    f[1][i]+=x,f[0][i]+=y;
                }
            }
            for(int i=1;i<=tot;++i) ans+=(f[1][i]-f[0][i])*tot/i;
        }
        return ans;
    }
};

//int main(){
//  vector<string> prob=
//  {"000000000000001000000",
//   "888999988889890999988",
//   "988889988899980889999",
//   "889898998889980999898",
//   "988889999989880899999",
//   "998888998988990989998",
//   "998988999898990889899"};
//  RandomPaintingOnABoard tmp;
//  LD ans=tmp.expectedSteps(prob);
//  printf("%.10Lf\n",ans);
//  return 0;
//}

猜你喜欢

转载自www.cnblogs.com/autoint/p/12129975.html