1. 程式人生 > >bzoj1076: [SCOI2008]獎勵關 狀壓dp

bzoj1076: [SCOI2008]獎勵關 狀壓dp

() rec ble 電子遊戲 bzoj1076 現在 得到 ios 負數

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

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

題解

首先這題要倒著掃(正著掃我寫掛了,理論上應該沒問題),倒著由合法情況推至下一個合法情況,避免掃不合法的。
很明顯狀壓n種寶箱,1代表打開了,0代表沒打開。
考慮第i次能不能從第i+1次打開第p個寶箱轉移過來,可以就由打開第p個寶箱和不打開寶箱中更優的轉移,否則直接由不打開寶箱轉移。

代碼

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include <iomanip>
using namespace std;
const int maxn=(1<<15)+10;
int K,n;
int w[20];
int nxt[20];
int ch[20][20];
double f[110][maxn];
double max(double a,double b){return a>b?a:b;}
int main(){
    cin>>K>>n;
    for(int i=1;i<=n;i++){
        cin>>w[i];
        int x;cin>>x;
        while(x){
            ch[i][x]=1;
            cin>>x;
        }
    }
    for(int i=1;i<=n;i++) for(int j=n;j>=1;j--)
        nxt[i]=(nxt[i]<<1)+ch[i][j];
    for(int i=K;i>=1;i--) for(int j=0;j<(1<<n);j++){ 
        for(int k=1;k<=n;k++){
            if((nxt[k]&j)==nxt[k])f[i][j]+=max(f[i+1][j],f[i+1][j|(1<<(k-1))]+w[k]);
            else f[i][j]+=f[i+1][j];
        }
        f[i][j]/=n;
    }

    cout.unsetf(ios::fixed);
    cout<<fixed<<setprecision(6)<<f[1][0]<<endl;
    return 0;
}

bzoj1076: [SCOI2008]獎勵關 狀壓dp