禮物(概率dp)
阿新 • • 發佈:2020-08-19
題目大意
夏川的生日就要到了。作為夏川形式上的男朋友,季堂打算給夏川買一些生 日禮物。
商店裡一共有種禮物。夏川每得到一種禮物,就會獲得相應喜悅值Wi(每種禮物的喜悅值不能重複獲得)。
每次,店員會按照一定的概率Pi(或者不拿出禮物),將第i種禮物拿出來。 季堂每次都會將店員拿出來的禮物買下來。沒有拿出來視為什麼都沒有買到,也算一次購買。
眾所周知,白毛切開都是黑的。所以季堂希望最後夏川的喜悅值儘可能地高。
求夏川最後最大的喜悅值是多少,並求出使夏川得到這個喜悅值,季堂的期望購買次數。
輸入格式
第一行,一個整數N,表示有N種禮物。
接下來N行,每行一個實數Pi和正整數Wi,表示第i種禮物被拿出來的概率和 可以獲得喜悅值。
輸出格式
第一行,一個整數表示可以獲得的最大喜悅值。
第二行,一個實數表示獲得這個喜悅值的期望購買次數,保留3位小數。
資料範圍
對於10%的資料,N = 1
對於30%的資料,N ≤ 5
對於100%的資料,N ≤ 20,0 < Wi ≤ 10^9 ,0 < Pi ≤ 1且∑Pi ≤ 1
注意:本題不設spj
演算法分析
- N最大隻有20 所以顯然可以用狀壓來做
- 若我們用1來表示買了 0表示未買 則我們照樣選擇倒著列舉 然後舉例說明
10101這樣一個狀態 再買一個東西可以轉移到 10111 或者 11101兩種
買到第二個位置的概率 乘上之前已經求過的 10111 的dp值 然後把每個0的位置都列舉一遍 求和 再加上( 1 - 所有0位置的概率和)(這是買空或者買到買過的東西的概率) × dp[當前狀態] 就好了 - 所以我們轉移方程寫出來應該是這個樣子的 f[i] = \(\sum\) (dp[j] * p[k]) + (1 - \(\sum\)p[k]) * f[i] + 1
移項之後可以得到 f[i] = (\(\sum\) (dp[j] * p[k]) + 1) \(\div\) (\(\sum\)p[k])
i 表示當前的狀態 j表示當前狀態再買一個之前沒買過的物品的後的可能狀態 k表示買到那一個物品的概率
Code
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 25; const int Max = 1 << 20; double p[maxn]; double f[Max]; int w[maxn]; ll sum; int main(){ int n;scanf("%d",&n); for(int i = 1;i <= n;++i){ int x;scanf("%lf%d",&p[i],&x); sum += x; } int S = (1 << n) - 2; for(int i = S;i >= 0;--i){ double sump = 0;//$\sum$p[k] double sum = 0;//$\sum$ (dp[j] * p[k]) for(int j = 1;j <= n;++j){//列舉買了第幾個物品 int cur = 1 << (j-1); if(cur & i)continue;//這個物品是否已經買過 sum += p[j] * f[cur | i];//f[cur|i] 一定在之前的時候求過了 sump += p[j];//讓買到新物品的概率累加 } f[i] = (sum + 1) * 1.0 / sump;//剛才推導的式子 } printf("%lld\n%.3lf\n",sum,f[0]); return 0; }