问题 A: 礼物
时间限制: 1 Sec 内存限制: 256 MB
题目描述
夏川的生日就要到了。作为夏川形式上的男朋友,季堂打算给夏川买一些生 日礼物。
商店里一共有种礼物。夏川每得到一种礼物,就会获得相应喜悦值Wi(每种 礼物的喜悦值不能重复获得)。
每次,店员会按照一定的概率Pi(或者不拿出礼物),将第i种礼物拿出来。 季堂每次都会将店员拿出来的礼物买下来。没有拿出来视为什么都没有买到,也 算一次购买。
众所周知,白毛切开都是黑的。所以季堂希望最后夏川的喜悦值尽可能地高。
求夏川最后最大的喜悦值是多少,并求出使夏川得到这个喜悦值,季堂的期 望购买次数。
输入
第一行,一个整数N,表示有N种礼物。
接下来N行,每行一个实数Pi和正整数Wi,表示第i种礼物被拿出来的概率和 可以获得喜悦值。
输出
第一行,一个整数表示可以获得的最大喜悦值。
第二行,一个实数表示获得这个喜悦值的期望购买次数,保留3位小数。
样例输入
3 0.1 2 0.2 5 0.3 7
样例输出
14 12.167
提示
对于10%的数据,N = 1
对于30%的数据,N ≤ 5
对于100%的数据,N ≤ 20 ,0 < Wi ≤ 10^9 ,0 < Pi ≤ 1且∑Pi ≤ 1
注意:本题不设spj
这道题基本两眼看出状压+概率DP然后就默默地推了大半天的转移方程也没推出来(好尴尬……)最后拿暴力默默地过了送分的第一个点。
其实这道题本质和 绿豆蛙的归宿 类似,都是求到达终点的步数(次数),我们大可通过状压搞一下他的每个状态,然后从所有物品都卖到的情况相回推那么转移方程就出来了
f[i]=Σp[k]*f[j](j为比i多买一件物品的状态,k就是多买的那件物品)+(1-∑p[k])(自己转移回自己的概率)*f[i]+1
很奇怪是吧,转移方程的右侧也出现了自身,我们只要把它稍微化简一下就可以了。
f[i]=(Σp[k]*f[j]+1)/(Σp[k])
然后就从最后向前推,答案就是f[0]。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<cmath> 6 #include<algorithm> 7 #include<queue> 8 #define N 25 9 using namespace std; 10 int n,va[N]; 11 double p[N],f[1<<20]; 12 long long sum; 13 int main() 14 { 15 scanf("%d",&n); 16 for(int i=1;i<=n;i++) 17 { 18 scanf("%lf%d",&p[i],&va[i]); 19 p[0]+=p[i]; 20 sum+=va[i]; 21 } 22 printf("%lld\n",sum); 23 for(int i=(1<<n)-2;i>=0;i--) 24 { 25 double sm=0.0; 26 for(int j=0;j<n;j++) 27 { 28 if(!((1<<j)&i)) 29 { 30 f[i]+=p[j+1]*f[i|(1<<j)]; 31 sm+=p[j+1]; 32 } 33 } 34 f[i]++; 35 f[i]/=sm; 36 } 37 printf("%.3lf\n",f[0]); 38 return 0; 39 }