BZOJ1076 [SCOI2008]奖励关 概率 状态压缩动态规划

欢迎访问~原文出处——博客园-zhouzhendong

去博客园看该题解


题目传送门 - BZOJ1076


题意概括

  有n个东西,k次扔出来。每次等概率扔出其中一个。

  你可以拿这个东西,但是有条件,得在拿到指定东西之后再拿,否则白拿。

  拿到一个东西,会获得其权值。可以是负数。


 

题解

  状压dp跑一发。

  一开始想写正着dp的,因为我觉得这样听挺容易想的。

  然而网上的大牛都说是倒着的。于是我倒着了。

  方程是这样的:

  dp[i][j]表示扔出来i次之后,状态为j,在这之后可以得到的最大收益。

  dp[i][j]=(∑F[k])/n

  F[k]分两种情况。如果在状态j的情况下可以取k,那么F[k] = max(dp[i+1][j],dp[i+1][j | (1<<k)] + v[k])

    否则F[k] = dp[i+1][j]

  根据意义,最后输出dp[0][0]即可。


 

代码

#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cmath>
using namespace std;
const int N=15+5,M=100+5,S=(1<<15)+5;
int n,m,v[N],pre[N];
double dp[M][S];
int main(){
    scanf("%d%d",&m,&n);
    memset(pre,0,sizeof pre);
    for (int i=0,p;i<n;i++){
        scanf("%d",&v[i]);
        while (scanf("%d",&p)&&p!=0)
            pre[i]|=1<<(p-1);
    }
    for (int i=m-1;i>=0;i--)
        for (int j=0;j<(1<<n);j++){
            dp[i][j]=0;
            for (int k=0;k<n;k++)
                if ((pre[k]&j)==pre[k])
                    dp[i][j]+=max(dp[i+1][j],dp[i+1][j|(1<<k)]+v[k]);
                else
                    dp[i][j]+=dp[i+1][j];
            dp[i][j]/=n;
        }
    printf("%.6lf",dp[0][0]);
    return 0;
}

 

posted @ 2017-08-14 14:35  zzd233  阅读(227)  评论(0编辑  收藏  举报