Loading

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;
}
posted @ 2021-07-12 08:50  hyl天梦  阅读(39)  评论(0编辑  收藏  举报