CF16E Fish 状压dp + 概率
其中 \(n\) 为小于 \(18\),一开始想着直接暴力枚举,但发现不行。然后就开始想 状压。
这个明显是开的下的。
我们令 \(0\) 表示还活着,\(1\) 表示死了。用一个二进制数去表示每一个局面,然后令 \(f_S\) 表示局面 \(S\) 所对应的概率是多少。
下面的 dp 我们用我为人人的做法。
显然,一个状态只能被比它小的状态更新,所以我们可以直接从小到大枚举每一个状态。
然后我们转移即可。
代码:
#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 20
#define M number
using namespace std;
const int INF=0x3f3f3f3f;
template<typename T> inline void read(T &x) {
x=0; int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
x*=f;
}
int n;
dd a[N][N],f[(1<<N)];
int main(){
// freopen("my.in","r",stdin);
// freopen("my.out","w",stdout);
read(n);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%lf",&a[i][j]);
f[0]=1;
for(int i=0;i<(1<<n)-1;i++){
int w=0;
for(int j=0;j<=n-1;j++) if(!((i>>j)&1)) w++;
if(w==1) continue;
for(int j=0;j<=n-1;j++){
if(!((i>>j)&1)){
int now=i|(1<<j);
for(int k=0;k<=n-1;k++){
if(k==j) continue;
if(!((i>>k)&1)){
f[now]+=f[i]*a[k+1][j+1]*(1/(dd)(w*(w-1)/2));
}
}
}
}
}
int all=(1<<n)-1;
for(int i=0;i<=n-1;i++){
printf("%lf ",f[all^(1<<i)]);
}
return 0;
}