1. 程式人生 > >【[SCOI2008]獎勵關】

【[SCOI2008]獎勵關】

又抄了一篇題解

要涼了要涼了,開學了我還什麼都不會

文化課涼涼,NOIP還要面臨爆零退役的歷史程序

這道題挺神的,期望+狀態壓縮

我們設\(dp[i][S]\)表示在第\(i\)天前,撿的寶物狀態為\(S\)到第\(K\)天結束的期望收益是多少

於是我們的答案是\(dp[1][0]\),也就是第一天前(就是沒開始)什麼寶物也沒有到結束的期望是多少

期望倒著推,我們的初始狀態就是\(dp[k+1]=\{0\}\)

之後對於一個狀態\(dp[i][S]\)

在第\(i\)\(m\)種寶物出現的概率都是\(1/m\)

所以列舉每一種寶物\(j\),如果這個寶物的前提條件被\(S\)包含

就有

\[dp[i][S]+=max(dp[i+1][S|(1<<(j-1))]+val[j],dp[i+1][S])*1/m\]

就是就算可以選擇這個寶物的話,我們也兩種選擇拿或者不拿這個寶物

如過沒有被包含,你只能不拿這個寶物

\[dp[i][S]+=dp[i+1][S]*1/m\]

程式碼

#include<iostream>
#include<cstdio>
#include<cstring>
#define re register
#define max(a,b) ((a)>(b)?(a):(b))
inline int read()
{
    char c=getchar();
    int x=0,r=1;
    while(c<'0'||c>'9') 
    {
        if(c=='-') r=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
      x=(x<<3)+(x<<1)+c-48,c=getchar();
    return x*r;
}
double dp[102][32769];
int n,m,N;
int cost[16];
int f[16];
int main()
{
    n=read(),m=read();
    for(re int i=1;i<=m;i++)
    {
        cost[i]=read();
        int x=read(),y=0;
        while(x) y|=1<<(x-1),x=read();
        f[i]=y;
    }
    N=(1<<m)-1;
    for(re int i=0;i<=N;i++) dp[n+1][i]=0;
    for(re int i=n;i;i--)
    for(re int j=0;j<=N;j++)
    {
        for(re int k=1;k<=m;k++)
        if(((f[k]|j)==j))
        {
            dp[i][j]+=max(dp[i+1][j],dp[i+1][j|(1<<(k-1))]+cost[k])/double(m);
        }else dp[i][j]+=dp[i+1][j]/double(m);
    }
    printf("%.6lf",dp[1][0]);
    return 0;
}