1. 程式人生 > >bzoj 1076: [SCOI2008]獎勵關 期望dp+狀態壓縮

bzoj 1076: [SCOI2008]獎勵關 期望dp+狀態壓縮

Description

  你正在玩你最喜歡的電子遊戲,並且剛剛進入一個獎勵關。在這個獎勵關裡,系統將依次隨機丟擲k次寶物,
每次你都可以選擇吃或者不吃(必須在丟擲下一個寶物之前做出選擇,且現在決定不吃的寶物以後也不能再吃)。
寶物一共有n種,系統每次丟擲這n種寶物的概率都相同且相互獨立。也就是說,即使前k-1次系統都丟擲寶物1(
這種情況是有可能出現的,儘管概率非常小),第k次丟擲各個寶物的概率依然均為1/n。 獲取第i種寶物將得到Pi
分,但並不是每種寶物都是可以隨意獲取的。第i種寶物有一個前提寶物集合Si。只有當Si中所有寶物都至少吃過
一次,才能吃第i種寶物(如果系統丟擲了一個目前不能吃的寶物,相當於白白的損失了一次機會)。注意,Pi可
以是負數,但如果它是很多高分寶物的前提,損失短期利益而吃掉這個負分寶物將獲得更大的長期利益。 假設你
採取最優策略,平均情況你一共能在獎勵關得到多少分值?

Input

  第一行為兩個正整數k和n,即寶物的數量和種類。以下n行分別描述一種寶物,其中第一個整數代表分值,隨
後的整數依次代表該寶物的各個前提寶物(各寶物編號為1到n),以0結尾。

Output

  輸出一個實數,保留六位小數,即在最優策略下平均情況的得分。

Sample Input

1 2

1 0

2 0
Sample Output

1.500000
HINT

【資料規模】

1<=k<=100,1<=n<=15,分值為[-10^6,10^6]內的整數。

分析:f[i,j]表示經過了i輪後選的物品狀態為j的期望得分
轉移十分簡單
一開始從前往後推發現沒法判斷方案是否合法然後就只好強行改成逆推咯

程式碼:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

int bin[20],need[20],val[20];
double f[105][65536];

int main()
{
    int n,m;
    bin[0]=1;
    for (int i=1;i<=16;i++)
        bin[i]=bin[i-1]*2;
    scanf
("%d%d",&m,&n); for (int i=1;i<=n;i++) { scanf("%d",&val[i]); int x; scanf("%d",&x); while (x) { need[i]+=bin[x-1]; scanf("%d",&x); } } for (int i=m;i>=1;i--) for (int j=0;j<=bin[n]-1;j++) for (int k=1;k<=n;k++) if ((j&need[k])==need[k]) f[i][j]+=max(f[i+1][j],f[i+1][j|bin[k-1]]+val[k])/n; else f[i][j]+=f[i+1][j]/n; printf("%.6lf",f[1][0]); return 0; }