jzoj5675 【GDOI2018Day1模拟4.20】锡林郭勒 (状压DP,暴搜求状态数上界)

Describe

有 n 个黑点与 m 个白点,其中第 i 个黑点与第 j 个白点之间有边的概率为 p i,j ,求期望最大匹配数。
对于 100% 的数据,n ≤ 5,m ≤ 1000。

DP

很显然是DP,而且应该与状压有关。 毕竟n这么小。
最初的想法是,要维护最大匹配,就对于最大匹配不能存在增广路。
这个想法有个缺陷:可能有多个匹配,如何将需要状态全部压起来?

其实很简单,不需要考虑增广路,直接将左侧所有可能匹配记为S (一个01状态)。即将多个最大匹配的左点状态存起来,例如是否存在一个将点{1,2}覆盖的匹配,是否存在将点{2,3}覆盖的匹配等。 (注意不一定最大的)
对应性:一种不同的连法只有一个对应状态。

这样每次枚举一个右侧点与其连边状态(2^5),再枚举旧状态S,先将S分解成若干个左点状态,然后考虑求S’.
首先显然S’包含S,那么对于某个左点状态,枚举每个不在其中的左点,假若这个点有边到当前右侧点,那么对应在S’中增加新状态。

答案也非常好计算,拿dp出来的f算一下sigma概率*最大匹配即可。

左点状态共有2^5个,那么S最多有2^(2^5)个?
注意到假如S中存在{1,2,3},那么{1,2},{1,3}…也一定存在。这似乎启示我们会有很多冗余状态?
按照转移方法写个暴搜,最终会发现当n=5时只有K=406个有效状态(数据来自题解)。
那么最终的时间: m(2n)K ,转移可以预处理,因为对于一个S与某个点的转移con,S’是固定的。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <map>
#include <algorithm>
#define getloc(x,y) (((x)&(1<<(y)))!=0)
using namespace std;
typedef double db;
typedef long long ll;

int n,m,stm,ztot,o;
int has[11][1001],cnt[100];

db p[11][1001];
db ans;
db f[2][500];

map<ll,ll> H;
ll idex[20000],nex[1000][50];
void makestatu(ll s) {
    if (H[s]!=0) return;
    idex[++stm]=s; H[s]=stm;
    int xxx=stm;
    for (int con=0; con<ztot; con++) {
        ll sp=s;
        for (int i=0; i<ztot; i++) if (getloc(s,i)) {
            for (int j=0; j<n; j++) if (getloc(i,j)==0 && getloc(con,j)==1) {
                sp=sp|(1<<(i|(1<<j)));
            }
        }
        makestatu(sp);
        nex[xxx][con]=H[sp];
    }
}
int main() {
    freopen("shilingol.in","r",stdin);
//  freopen("shilingol.out","w",stdout);
    cin>>n>>m; ztot=1<<n;
    for (int i=1; i<ztot; i++) cnt[i]=cnt[i/2]+(i&1);
    for (int i=1; i<=n; i++) for (int j=1; j<=m; j++) scanf("%lf",&p[i][j]);

    makestatu(1);
    f[o][H[1]]=1;

    for (int i=1; i<=m; i++) {
        o=1-o;
        memset(f[o],0,sizeof f[o]);

        for (int s=1; s<=stm; s++) if (f[1-o][s]>0) {
            for (int con=0; con<ztot; con++) {
                db gl=1;
                for (int z=0; z<n; z++) 
                    gl*=getloc(con,z)?p[z+1][i]:(1-p[z+1][i]);
                f[o][nex[s][con]]+=f[1-o][s]*gl;
            }
        }
    }
    for (int s=1; s<=stm; s++) {
        int mx=0; ll ss=idex[s];
        for (int i=0; i<ztot; i++) 
            if (getloc(ss,i)) mx=max(mx,cnt[i]);
        ans+=mx*f[o][s];
    }
    printf("%.6lf",ans);
}

猜你喜欢

转载自blog.csdn.net/jokerwyt/article/details/80077554