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种。
预处理转移,复杂度大概是 1000∗5∗1024∗32=1.6∗108
但是注意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);
}