题意:给你N种硬币,每种硬币有Si个,有Pi 概率朝上,每次抛所有硬币抛起,所有反面的拿掉,问每种硬币成为最后的lucky硬币的概率。
题解:都知道是概率dp,但是模拟赛时思路非常模糊,很纠结,dp[10][1e6]这个状态让我觉得丝毫没有用,当时一直以为每种硬币是互相独立的。然后中间吃了个饭,回来又想了很久很久,就是不想看题解,然后发现,这个dp和极限有关,同时状态跟走了几步有极大的关系。否则你初始值根本没法赋,那么显然我猜既然保留6位,肯定当数字小到一定程度时是可以忽略的,也就是进行的次数多了后。随意打了个表,0.6的36次小于1e-8,然而事实是这样还不够。好像要开到70多。然后就定义一个dp1[i][j]=(1-pij )^num[i]为进行了j轮后第i种硬币全无的概率(表示不看题解这个式子怎么算都不知道),那么1-dp1[i][j]就是还活着的概率。那么这个问题也就变成了第j轮后网上很多题解的那个式子
ans[i]=∑1~max (dp2[i][j]-dp2[i][j+1])*∏k!=i dp1[k][j] 我没考虑到的主要是要减掉 dp2[i][j+1],因为这里相当于是在第j这个位置就要结束游戏,所以要剪掉下一步还存活的概率。稍微还是有点不理解,概率题好难想啊。
#include<cstdio> #include<iostream> #include<bitset> #include<cstring> #include<string> #include<algorithm> #include<map> #include<queue> #include<set> #include<cmath> #define mp make_pair #define pb push_back #define ll long long #define lc no[x].ch[0] #define rc no[x].ch[1] #define pa no[x].fa #define db double #define ls x<<1 #define rs x<<1|1 #define For(i,a,b) for(int i=a;i<=b;i++) #define Forr(i,a,b) for(int i=a;i>=b;i--) using namespace std; const int maxn=80; int num[20]; double num1[20]; double dp1[20][100]; double dp2[20][100]; double ans[20]; int n; double q_pow(db vs,int t) { double res=1.0; while(t) { if(t&1) { res*=vs; } vs=vs*vs; t>>=1; } return res; } int main() { int T; cin>>T; while(T--) { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d%lf",&num[i],&num1[i]); } for(int i=1;i<=n;i++) { ans[i]=0.0; } for(int i=1;i<=n;i++) { for(int j=1;j<maxn;j++) { double st=q_pow(num1[i],j); // cout<<st<<endl; dp1[i][j]=q_pow(1-st,num[i]); dp2[i][j]=1-dp1[i][j]; } } for(int i=1;i<=n;i++) { for(int j=1;j<maxn;j++) { double tmp=1.0; for(int k=1;k<=n;k++) { if(k!=i)tmp*=dp1[k][j]; } ans[i]+=(dp2[i][j]-dp2[i][j+1])*tmp; } } if(n==1){printf("%.6f ",1.0);continue;} for(int i=1;i<n;i++) { printf("%.6f ",ans[i]); }printf("%.6f ",ans[n]); } }