1. 程式人生 > 實用技巧 >【SCOI2008】獎勵關 題解(狀壓DP+期望)

【SCOI2008】獎勵關 題解(狀壓DP+期望)

題目連結

題目大意:給定$n$個寶物,每次隨機丟擲一個寶物,獎勵分數為$p_i$。但如果選這個寶物必須選過它的前置寶物集合。共進行$K$輪問最優策略下的期望。

$n\leq 15,-10^6\leq p_i\leq 10^6$

--------------------------

看到資料範圍,狀壓很容易想到。

設$f[i][j]$表示到了第$i$輪,寶物取捨狀態為$j$的最大期望得分。

但這樣表示有一個問題:可能在第$i$輪沒法到達狀態$j$。我們無法預知未來。

改一下定義:$f[i][j]$表示第$1$輪到第$i-1$輪寶物取捨狀態為$j$,第$i$輪到第$K$輪的最大期望得分。有如下狀態轉移方程:

如果前置寶物都已經取過,那麼有$f[i][j]+=\max(f[i+1][j],f[i+1][j|(1<<(k-1))]+p[k])$

如果沒有,則直接繼承先前狀態:$f[i][j]+=f[i+1][j]$。

最後再除$n$即為期望值。

程式碼:

#include<bits/stdc++.h>
using namespace std;
int n,K,p[16],s[16],x;
double f[105][1<<16];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if
(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();} return x*f; } int main() { K=read(),n=read(); for (int i=1;i<=n;i++) { p[i]=read(); while(x=read()) s[i]=s[i]|(1<<x-1); } for (int i=K;i>=1;i--) for (int j=0;j<(1
<<n);j++) { for (int k=1;k<=n;k++) if ((j&s[k])==s[k]) f[i][j]+=max(f[i+1][j],f[i+1][j|(1<<k-1)]+p[k]); else f[i][j]+=f[i+1][j]; f[i][j]/=n; } printf("%lf",f[1][0]); return 0; }