【BZOJ】1076 [SCOI2008]奖励关 期望DP+状压DP

【题意】n种宝物,k关游戏,每关游戏给出一种宝物,可捡可不捡。每种宝物有一个价值(有负数)。每个宝物有前提宝物列表,必须在前面的关卡取得列表宝物才能捡起这个宝物,求期望收益。k<=100,n<=15。

【算法】期望DP+状压DP

【题解】主要需要记录的状态是前缀已有宝物,所以设f[i][S]表示前i关已有宝物列表S的期望收益。

根据全期望公式,依赖于第i+1关的宝物选择:(如果列表符合)

$$f[i][S]=sum_{i=1}^{n}frac{1}{n}*Max(f[i+1][S'],f[i+1][S]) , S'=S|(1<<(i-1))$$

倒推是因为已知前缀列表S的情况下,很容易判断下一关宝物是否可捡。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int inf=100000;
int a[20],n,m,v[20];
double f[200][70000];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&v[i]);
        int u;scanf("%d",&u);
        a[i]=0;
        while(u!=0)
        {
            a[i]|=(1<<(u-1));
            scanf("%d",&u);
        }
    }
    int maxe=(1<<m)-1;
    for(int i=n-1;i>=0;i--)
    {
        for(int j=0;j<=maxe;j++)
        {
            f[i][j]=0;
            for(int k=1;k<=m;k++)
            {
                if((a[k]&j)==a[k])f[i][j]+=max(f[i+1][j],f[i+1][j|(1<<(k-1))]+v[k]);
                else f[i][j]+=f[i+1][j];
            }
            f[i][j]/=m;
        }
    }
    printf("%.6lf",f[0][0]);
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/onioncyc/p/7220082.html