【GDOI2018Day1模拟4.20】锡林郭勒(状压dp)

Description:

锡林郭勒坐落于美国加州,与赤峰、二连浩特、巴彦卓尔一起组成了闻名世界的科技集散地“硅谷”。在刚果河的滋润下,这里的葡萄酒产业也是美誉世界。夜晚将至,人们纷纷前往摩尔庄园参加宴会。夏威夷吉他的律动,拨动着夜晚的树影婆娑,跳跃着夜空的星河璀璨,引人们结伴共舞。
庄园主人阿金咔咔为了助兴,向来宾们抛出了一个问题:有 n 个黑点与 m 个白点,其中第 i 个黑点与第 j 个白点之间有边的概率为 p i,j ,求期望最大匹配数。

n ≤ 5,m ≤ 1000

题解:

观察数据范围,发现n较小,可能和状态压缩dp有关,而m较大,于是想到以m作为第一层状态。

先设 fi,j 表示右边的m个点确定了前i个,最大匹配数是j。

这显然无法转移。

可以再设一个S表示最大匹配可能是哪些。

这个S最大是 2C35 =1024种。

预处理转移,复杂度大概是 10005102432=1.6108

但是注意S有冗余的,所以在递推转移时判断当前状态的值是否为0,这样复杂度就能过了。

Code:

#include<cstdio> 
#include<cstring>
#include<algorithm>
#define db long double
#define low(a) ((a) & -(a))
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define max(a, b) ((a) > (b) ? (a) : (b))
using namespace std;


int n, m, a2[11], o, s[1025], d[6][1025], num[1025], ii, jj, kk;

int z1[6][1025][32], z2[6][1025][31], bz[32], mx;

db p[6][1025], f[2][6][1025], ans;

void dg(int x, int y, db s) {
    if(x > n) {
        f[o][z2[jj][kk][y]][z1[jj][kk][y]] += s * f[!o][jj][kk];
        return;
    }
    dg(x + 1, y | a2[x - 1], s * p[x][ii]);
    dg(x + 1, y, s * (1 - p[x][ii]));
}

int main() {
    freopen("shilingol.in", "r", stdin);
    freopen("shilingol.out", "w", stdout);
    a2[0] = 1; fo(i, 1, 10) a2[i] = a2[i - 1] * 2;

    scanf("%d %d", &n, &m);

    fo(i, 0, 1023) {int j = i; while(j) s[i] ++, j -= low(j);}
    fo(i, 0, a2[n] - 1) d[s[i]][++ d[s[i]][0]] = i, num[i] = d[s[i]][0];
    fo(i, 0, n) fo(j, 0, a2[d[i][0]] - 1) fo(k, 0, a2[n] - 1) {
        memset(bz, 0, sizeof bz); mx = i;
        fo(u, 0, d[i][0] - 1) if(j & a2[u]) {
            int w = d[i][u + 1]; bz[w] = 1;
            fo(p, 0, n - 1) if(k & a2[p])
                bz[w | a2[p]] = 1, mx = max(mx, s[w | a2[p]]);
        }
        z2[i][j][k] = mx;
        fo(u, 0, a2[n] - 1) if(s[u] == mx && bz[u])
            z1[i][j][k] += a2[num[u] - 1];
    }
    fo(i, 1, n) fo(j, 1, m) scanf("%Lf", &p[i][j]);
    f[o][0][1] = 1;
    fo(i, 1, m) {
        o = !o; memset(f[o], 0, sizeof f[o]);
        fo(j, 0, n) fo(k, 0, a2[d[j][0]] - 1) if(f[!o][j][k] > 1e-10){
            ii = i; jj = j; kk = k;
            dg(1, 0, 1);
        }

    }
    fo(j, 0, n) fo(k, 0, a2[d[j][0]] - 1) ans += f[o][j][k] * j;
    printf("%.6Lf", ans);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值