1. 程式人生 > >P2473 [SCOI2008]獎勵關卡

P2473 [SCOI2008]獎勵關卡

大力模擬各種情況 k * n 的模擬出每次可能拋什麼出來 設f[k][s] 為前k個物品狀態為s時得分最大值 但是有問題啊,比如 113 和 133,狀態都是f[3][101],難道我還要多開一維存下不同情況?,不行吧?

還是說取最大的?,這相當於後面選擇的物品,對前面的狀態強行選擇了一下,但是並不是說113比133大,我就捨棄133,我捨棄133就相當於,走到這一步,後面直接都不走了,如果說我們要取最優解,顯然可以取最大的,捨棄掉不優的,但是對於一種需要遞推,或者是求期望的這種要求把所有可能都走一遍取平均值的,不能捨棄吧?捨棄這個狀態,就好像求平均數的時候,你捨棄了幾個比較小的數,那麼無論如何這個平均數都是錯誤的吧?

這種轉移感覺就很麻煩的,試著倒推,不是說拿東西麼,我們把過程倒過來,扔東西,從第n個階段每個階段扔一個東西並獲得收益,也就是說f[k][s]表示前k個,狀態為s時第k+1 ~ n輪獲得的期望收益 答案就是f[1][0]

期望的和等於和的期望是劃分階段的關鍵條件,因為不同階段的期望相加之後就是整個過程的期望,所以可以遞推

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
#define debug(x) cerr << #x << "=" << x << endl;
const int MAXN = 100000 + 10; struct object{ double val; int si; }ob[MAXN]; double ans,f[101][1<<15]; int n,k; int main() { scanf("%d%d", &k, &n); for(int i=1; i<=n; i++) { cin >> ob[i].val; int si = 0; while(1) { int pos = 0; scanf("%d", &pos); if(!pos) break;
si |= 1 << (pos-1); } ob[i].si = si; } for(int i=k; i; i--) { for(int j=0; j<1<<n; j++) { for(int x=1; x<=n; x++) { int si = ob[x].si; if((j&si) == si) { f[i][j] += max(f[i+1][j], f[i+1][j|(1<<(x-1))] + ob[x].val); } else { f[i][j] += f[i+1][j]; } } f[i][j] /= n; } } printf("%.6lf", f[1][0]); return 0; }