1. 程式人生 > >BZOJ 1076: [SCOI2008]獎勵關(期望+狀壓DP)

BZOJ 1076: [SCOI2008]獎勵關(期望+狀壓DP)

傳送門

題解

不能從前往後推,求期望的正確姿勢應該是從後往前。

每個物品有先決限制,我們將已獲得的物品狀壓起來。記f[i][s]為第i關開始前擁有集合s的得分期望。

列舉關卡數、集合和第i關的物品。若滿足先決條件,則f[i][s] += max(f[i+1][s], f[i+1][s|(1<<(j-1))]+Val[j]) ,否則f[i][s] += f[i+1][s],最後乘上概率1/n。f[1][0]就是答案。

由於有點虛空間,開了滾動陣列,別忘了清空。

程式碼

#include <cstdio>
#include <cstdlib>
#include <iostream> #include <cmath> #include <algorithm> #include <cstring> #define maxn 16 using namespace std; double dp[2][1<<maxn]; int S[maxn], Val[maxn]; int n, k; int main(){ scanf("%d%d", &k, &n); int x; for(int i = 1; i <= n; i++){ scanf
("%d", &Val[i]); for(;;){ scanf("%d", &x); if(!x) break; S[i] |= (1 << (x-1)); } } x = 0; for(int i = k; i >= 1; i--){ x ^= 1; for(int s = 0; s < (1<<n); s++){ dp[x][s] = 0.0; for
(int j = 1; j <= n; j++){ if((s & S[j]) != S[j]) dp[x][s] += dp[x^1][s]; else dp[x][s] += max(dp[x^1][s], dp[x^1][s|(1<<(j-1))]+(double)Val[j]); } dp[x][s] /= n; } } printf("%.6lf\n", dp[x][0]); return 0; }