一道看似挺简单的概率题,考试时硬是连样例都没模出来,最后暴力过了第一个点还是太菜了,QAQ......
概率这方面一直很不擅长,希望以后考试能多总结些。
首先这题看到数据范围可以很容易联想到状压,2^20不会爆炸
我们设f[i]表示i状态下距离买全还有多少期望次数,
设0表示已经买了,1表示没买
那么显然概率DP都是习惯倒序求解的,表示当前状态到最终状态还剩多少。。。
此题中显然f[0]表示已经买完了,so f[0]=0
这里我们从1~(1<<n)-1枚举(因为1,0含义的原因,代码是正序枚举的QAQ)
然后每个状态例如:
f[101]可有(f[001]+1)*p[1]+(f[100]+1)*p[3]转移表示还没买了一三物品时由还没买一,还没买三
转移,期望由概率*移动一次的步数转移,又因为期望可加所以可以加步数
但还有一种少考虑的情况当前可能没买所以还要加上(f[101]+1)*(1-∑p[i])(p[i]是还没买物品即1,3的概率相加)
然后就愉快的DP了
方程f[i]=(f[i]+1)*(1-∑p[i])+∑(f[i异或以后上每一位能异或的1]+1)*p[i].
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<string> 5 #include<algorithm> 6 #include<vector> 7 #include<cmath> 8 using namespace std; 9 #define MAXN (1<<21)+1 10 #define ll long long 11 double f[MAXN],p[MAXN]; 12 ll kai[MAXN]; 13 ll n; 14 ll base[MAXN]; 15 double lian;ll kkk; 16 void pt(ll x) 17 { 18 if(x) 19 { 20 pt(x>>1); 21 printf("%lld",x&1); 22 } 23 return ; 24 } 25 int main() 26 { 27 scanf("%lld",&n); 28 for(ll i=1;i<=n;++i) 29 { 30 cin>>p[i]; 31 scanf("%lld",&kai[i]); 32 lian+=p[i]; 33 kkk+=kai[i]; 34 } 35 base[0]=1; 36 for(ll i=1;i<=n;++i) 37 { 38 base[i]=(base[i-1]<<1);//printf("base[%lld]=%lld ",i,base[i]); 39 } 40 f[0]=0; 41 for(ll i=1;i<=base[n]-1;++i) 42 { 43 // printf("--------------------- "); 44 ll th=i; 45 double kx=0.0; 46 double lian=0.0; 47 for(ll k=1;k<=20;++k) 48 { 49 if((th>>(k-1)&1)==1) 50 { 51 // printf("th");pt(th);printf(" "); 52 // printf("base");pt(base[k-1]);printf(" "); 53 kx+=(f[th^base[k-1]]+1)*p[k]; 54 lian+=p[k]; 55 //printf("gai f[%lld]=%.4lf ",th^base[k-1],f[th^base[k-1]]); 56 } 57 } 58 f[i]+=(kx/lian)+(1-lian)/lian; 59 // printf("f[%lld]=%.4lf lian=%.4lf ",i,f[i],lian); 60 } 61 printf("%lld %.3lf ",kkk,f[base[n]-1]); 62 }
2019-07-16于hz