[bzoj2891]匹配难题
对于\(S\subseteq \{T\mid T\subseteq [1,A]\}\),定义\(f_{i,S}\)表示左侧(对右侧前\(i\)个点)存在完美匹配的集合为\(S\)的概率
从\(f_{i-1,S}\)转移到\(f_{i,S'}\)时枚举\(i\)的出边\(O\subseteq [1,n]\),则\(S'=S\cup \{T\mid \exists j\in O\cap T,T\backslash\{j\}\in S\}\)
爆搜可得(当\(A=6\)时)可能的\(S\)仅有\(3762\)种,时间复杂度为\(O(3762\cdot 2^{A}B)\)
#include<bits/stdc++.h>
using namespace std;
#define N 105
#define M 40000
#define ull unsigned long long
int n,m,t;double ans,p[N][6],P[1<<6],g[M],f[M];
ull HSi[6],tr[M][1<<6];queue<ull>q;
map<ull,int>id;map<ull,int>::iterator it;
int main(){
scanf("%d%d",&n,&m);
if (n<=m){
for(int i=0;i<n;i++)
for(int j=1;j<=m;j++)scanf("%lf",&p[j][i]);
}
else{
swap(n,m);
for(int j=1;j<=m;j++)
for(int i=0;i<n;i++)scanf("%lf",&p[j][i]);
}
for(int i=0;i<n;i++)
for(int S=0;S<(1<<n);S++)
if ((S>>i)&1^1)HSi[i]|=((ull)1<<S);
t=id[1]=1,q.push(1);
while (!q.empty()){
ull HS=q.front();q.pop();
for(int S=0;S<(1<<n);S++){
ull HS0=HS;
for(int i=0;i<n;i++)
if ((S>>i)&1)HS0|=((HS&HSi[i])<<(1<<i));
if (!id[HS0])id[HS0]=++t,q.push(HS0);
tr[id[HS]][S]=id[HS0];
}
}
f[1]=1;
for(int i=1;i<=m;i++){
for(int S=0;S<(1<<n);S++){
P[S]=1;
for(int j=0;j<n;j++)P[S]*=((S>>j)&1 ? p[i][j] : 1-p[i][j]);
}
memcpy(g,f,sizeof(f));
memset(f,0,sizeof(f));
for(int HS=1;HS<=t;HS++)
for(int S=0;S<(1<<n);S++)f[tr[HS][S]]+=P[S]*g[HS];
}
for(it=id.begin();it!=id.end();it++){
int mx=0;
for(int S=0;S<(1<<n);S++)
if (((*it).first>>S)&1)mx=max(mx,__builtin_popcount(S));
ans+=mx*f[(*it).second];
}
printf("%.2f\n",ans);
return 0;
}