记$HS$为(左部点中)所有存在完美匹配的点集(所构成的集合)
定义$f_{i,HS}$表示右部前$i$个点且状态为$HS$的概率,考虑转移——
记$i$的边集为$N$,枚举所匹配的点,可得$HS'=HS\cup \{S\cup \{x\}\mid x\in N,S\in HS\}$
关于如何求$HS$,可以用$2^{n}$位二进制表示(map存储),并做到单次$o(a)$(位运算)
换言之,转移即$P(i,N)\cdot f_{i-1,HS}\rightarrow f_{i,HS}$(其中$P(i,N)$表示$i$边集为$N$的概率)
同时,从$HS=\{\empty\}$出发,经过上述转移后不同的$HS$仅有$f(a)\le 3762$种
总复杂度为$o(2^{a}f(a)\times (a+\log f(a)+b))$,可以通过
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 105 4 #define M 40000 5 #define ull unsigned long long 6 int n,m,t;double ans,p[N][6],P[1<<6],g[M],f[M]; 7 ull HSi[6],tr[M][1<<6];queue<ull>q; 8 map<ull,int>id;map<ull,int>::iterator it; 9 int main(){ 10 scanf("%d%d",&n,&m); 11 if (n<=m){ 12 for(int i=0;i<n;i++) 13 for(int j=1;j<=m;j++)scanf("%lf",&p[j][i]); 14 } 15 else{ 16 swap(n,m); 17 for(int j=1;j<=m;j++) 18 for(int i=0;i<n;i++)scanf("%lf",&p[j][i]); 19 } 20 for(int i=0;i<n;i++) 21 for(int S=0;S<(1<<n);S++) 22 if ((S>>i)&1^1)HSi[i]|=((ull)1<<S); 23 t=id[1]=1,q.push(1); 24 while (!q.empty()){ 25 ull HS=q.front();q.pop(); 26 for(int S=0;S<(1<<n);S++){ 27 ull HS0=HS; 28 for(int i=0;i<n;i++) 29 if ((S>>i)&1)HS0|=((HS&HSi[i])<<(1<<i)); 30 if (!id[HS0])id[HS0]=++t,q.push(HS0); 31 tr[id[HS]][S]=id[HS0]; 32 } 33 } 34 f[1]=1; 35 for(int i=1;i<=m;i++){ 36 for(int S=0;S<(1<<n);S++){ 37 P[S]=1; 38 for(int j=0;j<n;j++)P[S]*=((S>>j)&1 ? p[i][j] : 1-p[i][j]); 39 } 40 memcpy(g,f,sizeof(f)); 41 memset(f,0,sizeof(f)); 42 for(int HS=1;HS<=t;HS++) 43 for(int S=0;S<(1<<n);S++)f[tr[HS][S]]+=P[S]*g[HS]; 44 } 45 for(it=id.begin();it!=id.end();it++){ 46 int mx=0; 47 for(int S=0;S<(1<<n);S++) 48 if (((*it).first>>S)&1)mx=max(mx,__builtin_popcount(S)); 49 ans+=mx*f[(*it).second]; 50 } 51 printf("%.2f\n",ans); 52 return 0; 53 }