kuangbin 带你飞 概率期望
正推不行就逆推!
经典问题:生日悖论
换成其互斥事件:m个人, 每个人生日都不相同的概率 ≤ 0.5 时最小人数。
这就是邮票收集问题的变形:每个邮票至少出现一次的概率 小于等于 0.5
邮票收集问题资料:https://en.wikipedia.org/wiki/Coupon_collector%27s_problem
我们现在面对的是一个n面的骰子, 骰子的每面都是随机出现的, 求问将所有面都被看完所期望的投掷次数(假设只看最上面那一面)
那么, 问题的解就是:
H[n] = (1 + 1/2 + 1/3 + 1/4 + ... + 1/n), 这就是调和级数的前n项。
这个值近似等于欧拉常数约为:0.57721566490153286060651209。(不过这是一个当n接近无穷时的近似值, 并不能代替具体的H[n], 比如当 n = 1 || 2时)
而所求的是期望的权值, 根据期望的线性性质E(XY) = E(X)*E(Y)
所以, 总的权值期望就等价于 每次的权值期望 * 次数的期望。
n个面, 每个面至少出现一次的期望次数是:E(x) = n * H[n],那么, 某个指定的面至少出现一次的期望次数就是E(z) = E(x)/n = H[n]。
Light Oj 1027 A Dangerous Maze
题意 : 在n个门前选择一扇门出去, 然后如果第i扇门的 Xi值是正的话,你会花费Xi时间后出去 , 如果Xi是负数的话你会花费-Xi时间后回到老地方,并且忘记了刚才的选择, 选择一扇门的概率是等概的。求出去的期望。
思路 :定义一次选择选择到Xi是整数的概率为P1,选择到负数的概率是P2,然后选择了正数后平均在T1时间后出去, 选择了负数后平均在T2时间后回到原地。接着设出去的期望是Y,那么可以写出一个式子 :Y = P1 * T1 + P2 * (T2 + Y), 这样的话问题就得到了解决, 最后整理下式子就是 : Y = 正数个数的倒数 * ∑abs(Xi) ;
#include <map> #include <set> #include <list> #include <cmath> #include <ctime> #include <deque> #include <stack> #include <queue> #include <cctype> #include <cstdio> #include <string> #include <vector> #include <climits> #include <cstdlib> #include <cstring> #include <iostream> #include <algorithm> #define LL long long #define PI 3.1415926535897932626 using namespace std; LL gcd(LL a, LL b) {return a % b == 0 ? b : gcd(b, a % b);} LL tot,sum; int main() { int T,kase = 1; scanf("%d",&T); while (T--) { tot = 0; sum = 0; int N; scanf("%d",&N); for (int i = 1 ; i <= N ; i++) { int x; scanf("%d",&x); if (x > 0) tot++; sum += abs(x); } if (tot == 0) printf("Case %d: inf\n",kase++); else { LL g = gcd(sum,tot); printf("Case %d: %lld/%lld\n",kase++,sum / g,tot / g); } } return 0; }
LightOj 1030 Discovering Gold
#include <map> #include <set> #include <list> #include <cmath> #include <ctime> #include <deque> #include <stack> #include <queue> #include <cctype> #include <cstdio> #include <string> #include <vector> #include <climits> #include <cstdlib> #include <cstring> #include <iostream> #include <algorithm> #define LL long long #define PI 3.1415926535897932626 using namespace std; LL gcd(LL a, LL b) {return a % b == 0 ? b : gcd(b, a % b);} const int MAXN = 110; double dp[MAXN],val[MAXN]; int main() { int T,kase = 1; cin >> T; while (T--) { memset(dp,0,sizeof(dp)); int N; cin >> N; for (int i = 1 ; i <= N ; i++) cin >> val[i]; dp[N] = val[N]; for (int i = N - 1 ; i >= 1 ; i--) { dp[i] = val[i]; double cnt = min(6.0,1.0 * (N - i)); for (int j = 1 ; j <= cnt ; j++) dp[i] += dp[i + j] * 1.0 / cnt; } printf("Case %d: %.8lf\n",kase++,dp[1]); } return 0; }
LightOj 1038 Race to 1 Again
比较简单枚举因子就可以了
dp[x] = (dp[fac1] + 1) / cnt + (dp[fac2] +1)/ cnt + (dp[x] + 1) / cnt
#include <map> #include <set> #include <list> #include <cmath> #include <ctime> #include <deque> #include <stack> #include <queue> #include <cctype> #include <cstdio> #include <string> #include <vector> #include <climits> #include <cstdlib> #include <cstring> #include <iostream> #include <algorithm> #define LL long long #define PI 3.1415926535897932626 using namespace std; int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);} const int MAXN = 100010; vector<int>res[MAXN]; double dp[MAXN]; void init() { for (int i = 0 ; i < MAXN ; i++) res[i].clear(); for (int i = 1 ; i < MAXN ; i++) { for (int j = i ; j < MAXN ; j += i) res[j].push_back(i); } } double calcu(int x) { if (x == 1) return 0.0; else if (dp[x] >= 0) return dp[x]; int tot = res[x].size(); double ans = 1.0 * tot; for (int i = 0 ; i < tot - 1; i++) { ans += calcu(res[x][i]); } return dp[x] = ans / (tot - 1.0); } int main() { init(); memset(dp,-1,sizeof(dp)); int T,kase = 1; scanf("%d",&T); while (T--) { int N; scanf("%d",&N); double ret = calcu(N); printf("Case %d: %.8lf\n",kase++,ret); } return 0; }
LightOj 1079 Just another Robbery
#include <map> #include <set> #include <list> #include <cmath> #include <ctime> #include <deque> #include <stack> #include <queue> #include <cctype> #include <cstdio> #include <string> #include <vector> #include <climits> #include <cstdlib> #include <cstring> #include <iostream> #include <algorithm> #define LL long long #define PI 3.1415926535897932626 using namespace std; int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);} const int MAXN = 110; const double INF = 1e10; int N,M; double P; struct node { double p; int val; }src[MAXN]; double dp[MAXN][MAXN * MAXN]; int main() { int T,kase = 1; scanf("%d",&T); while (T--) { scanf("%lf%d",&P,&N); for (int i = 1 ; i <= N ; i++) scanf("%d%lf",&src[i].val,&src[i].p); for (int i = 0 ; i < MAXN ; i++) for (int j = 0 ; j < MAXN * MAXN ; j++) dp[i][j] = INF; dp[0][0] = 0; for (int i = 1 ; i <= N ; i++) { for (int j = 0 ; j < MAXN * MAXN ; j++) { dp[i][j] = min(dp[i - 1][j],dp[i][j]); if (j >= src[i].val && dp[i - 1][j - src[i].val] < INF) { dp[i][j] = min(dp[i][j],dp[i - 1][j - src[i].val] + (1 - dp[i - 1][j - src[i].val]) * src[i].p); //printf("dp[%d][%d] = %lf\n",i,j,dp[i][j]); } } } int ret = 0; for (int i = MAXN * MAXN - 1 ; i >= 0 ; i--) { if (dp[N][i] <= P) { printf("Case %d: %d\n",kase++,i); break; } } } return 0; }
LightOj 1104 Birthday Paradox
题意:
若一年有n天, 问至少需要多少个人才能满足其中两个人生日相同的概率大于等于0.5?
思路:
经典问题:生日悖论
换成其互斥事件:m个人, 每个人生日都不相同的概率 ≤ 0.5 时最小人数。
这就是邮票收集问题的变形:每个邮票至少出现一次的概率 小于等于 0.5
等价于:
找到最小的n, 使得:H[n] = (n / n * (n - 1) / n * (n - 2) / n * ... * (n - i) / n) <= 0.5
就是求所有人生日都不同的概率小于等于0.5(那么至少两个人同一天就是大于等于0,5);
加入一年365天.那么10个人全都不同天生日的概率就是
366/366 * 365/366 * 364/366 .... * 356/366;
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; int n; int main() { int t; int cas = 1; scanf("%d",&t); while(t--) { scanf("%d",&n); double res = 1; int i; for(i = n - 1; i >= 0; i--) { double tmp = i; res *= (tmp / n); if(res <= 0.5) break; } printf("Case %d: %d\n",cas++, n - i); } }
LightOj 1151 Snakes and Ladders
复制一波题解:
dp[i] 表示 在格子i时,结束游戏需要抛掷骰子的次数
当i处有snake或者ladders时
dp[i]=dp[go[i]]
否则
dp[i]=dp[a1]+dp[a2]+...+dp[an]+6cnt
因为题目中的一句话,如果抛骰子然后出界的话,需要重新再抛掷,所以转移到的位置个数不是6个,但是代价一定是6个(除a1->an以外的点被忽略掉,但是骰子还是抛掷了,所以次数还是要加上去)
发现方程中存在环,所以用高斯消元来解方程
必然存在环的。由于传送的特殊形式
#include <map> #include <set> #include <list> #include <cmath> #include <ctime> #include <deque> #include <stack> #include <queue> #include <cctype> #include <cstdio> #include <string> #include <vector> #include <climits> #include <cstdlib> #include <cstring> #include <iostream> #include <algorithm> #define LL long long #define PI 3.1415926535897932626 using namespace std; int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);} const int MAXN = 110; const double eps = 1e-8; int pos[MAXN]; struct Matrix { int equ,var; double mat[MAXN][MAXN]; double x[MAXN]; void build() { memset(mat,0,sizeof(mat)); memset(x,0,sizeof(x)); mat[100][100] = 1; x[100] = 0; for (int i = 1 ; i < 100 ; i++) { if (pos[i] == i) { int cnt = 0; for (int j = 1 ; j <= 6 ; j++) { if (i + j <= 100) { cnt++; mat[i][i + j] = -1; } } mat[i][i] = 1.0 * cnt; x[i] = 6.0; } else { x[i] = 0; mat[i][i] = 1; mat[i][pos[i]] = -1; } } equ = var = 100; } int gauss() { int i,j,k,col,max_r; for (k = 0 ,col = 0 ; k < equ && col < var ; k++,col++) { max_r = k; for (i = k + 1 ; i < equ ; i++) if (fabs(mat[i][col]) > fabs(mat[max_r][col])) max_r = i; if (fabs(mat[max_r][col]) < eps) continue; if (k != max_r) { for (j = col ; j < var ; j++) swap(mat[k][j],mat[max_r][j]); swap(x[k],x[max_r]); } x[k] /= mat[k][col]; for (j = col + 1 ; j < var ; j++) mat[k][j] /= mat[k][col]; mat[k][col] = 1; for (int i = 0 ; i < equ ; i++) { if (i != k) { x[i] -= x[k] * mat[i][k]; for (j = col + 1 ; j < var ; j++) mat[i][j] -= mat[k][j] * mat[i][col]; mat[i][col] = 0; } } } return 1; } }slover; int main() { int T,kase = 1; scanf("%d",&T); while (T--) { for (int i = 1 ; i <= 100 ; i++) pos[i] = i; int N; scanf("%d",&N); for (int i = 1 ; i <= N ; i++) { int u,v; scanf("%d%d",&u,&v); pos[u] = v; } slover.build(); slover.gauss(); printf("Case %d: %.12lf\n",kase++,slover.x[1]); } return 0; }
LightOj 1248 Dice (III)
思路:设dp[i]为已经扔出了i个不同面值,还要扔dp[i]次才能扔出n个不同面的期望次数,显然dp[n] = 0,要求dp[0]
则dp[i] = 1+ i/n * dp[i] + (n-i)/n * dp[i+1],本来要扔的一次加上各个状态转移的期望值
=>dp[i] = n / (n-i) + dp[i+1]
#include <map> #include <set> #include <list> #include <cmath> #include <ctime> #include <deque> #include <stack> #include <queue> #include <cctype> #include <cstdio> #include <string> #include <vector> #include <climits> #include <cstdlib> #include <cstring> #include <iostream> #include <algorithm> #define LL long long #define PI 3.1415926535897932626 using namespace std; int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);} const int MAXN = 100010; const double eps = 1e-8; double dp[MAXN]; int main() { int kase = 1,T; cin >> T; while (T--) { int n; cin >> n; dp[0] = 0; for (int i = 0 ; i < n ; i++) dp[i + 1] = dp[i] + 1.0 * n / (double)(n - i); printf("Case %d: %.8lf\n",kase++,dp[n]); } return 0; }
LightOj 1265 Island of Survival
在孤岛生存, 孤岛上有t头老虎,d头鹿, 每天会出现随机出现两只生物(包括你自己), 如果出现了一只老虎,那么你将被吃掉, 如果两只老虎, 则两只老虎会同归于尽,其他情况你都将生存下来。
当孤岛上没有老虎时, 就视为你生存成功。
问你生存成功的最大概率。
#include <map> #include <set> #include <list> #include <cmath> #include <ctime> #include <deque> #include <stack> #include <queue> #include <cctype> #include <cstdio> #include <string> #include <vector> #include <climits> #include <cstdlib> #include <cstring> #include <iostream> #include <algorithm> #define LL long long #define PI 3.1415926535897932626 using namespace std; int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);} const int MAXN = 1010; double dp[MAXN][MAXN]; int main() { int T,kase = 1; scanf("%d",&T); while (T--) { int t,d; scanf("%d%d",&t,&d); for (int i = 0 ; i <= t ; i++) for (int j = 0 ; j <= d ; j++) dp[i][j] = 0.0; for (int i = 0 ; i <= d ; i++) dp[0][i] = 1.0; for (int i = 1 ; i <= t ; i++) { for (int j = 0 ; j <= d ; j++) { double r1 = 0,r2 = 0; int tot = i + j + 1; if (i >= 2) r1 += dp[i - 2][j] * (double)(i * i - i) / (double)(tot * tot - tot); if (j >= 1) r1 += dp[i][j - 1] * (double)(2 * i * j) / (double)(tot * tot - tot); double p = 0.0; if (j >= 2) p = (double)(j * j - j) / (double)(tot * tot - tot); if (j >= 1) { r2 = r1; r2 += dp[i][j - 1] * (double)(j * 2) / (double)(tot * tot - tot); r2 /= (1 - p); } r1 /= (1 - p - (double)(j * 2) / (double)(tot * tot - tot)); dp[i][j] = max(r1,r2); } } printf("Case %d: %.12f\n", kase++, dp[t][d]); } return 0; }
LightOj 1274 Beating the Dataset
给一个文档, 这个文档由yes 、no 组成, 共有s个byte, 共有n个yes、no。
假设yes的个数为yes_num, no的个数为no_num。
将这n个数进行排列, 对于每个排列, 将其右移一个结果, 并在最左端补上yes, 再将其与原排列进行对比, 看有多少个不同的。
计算所有排列中 不同结果的平均次数。
#include <map> #include <set> #include <list> #include <cmath> #include <ctime> #include <deque> #include <stack> #include <queue> #include <cctype> #include <cstdio> #include <string> #include <vector> #include <climits> #include <cstdlib> #include <cstring> #include <iostream> #include <algorithm> #define LL long long #define PI 3.1415926535897932626 using namespace std; int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);} const int MAXN = 5010; int N,S; double dp[2][MAXN][2]; int main() { int T,kase = 1; scanf("%d",&T); while (T--) { scanf("%d%d",&N,&S); int right = S - 2 * N; int wa = 3 * N - S; memset(dp,0,sizeof(dp)); int cur = 0; for (int i = 1 ; i <= N ; i++) { int nxt = cur ^ 1; for (int j = 0 ; j <= min(right,i) ; j++) { double p1 = (double)j / i; double p2 = 1.0 - p1; if (j >= 1) { dp[nxt][j][1] = (dp[cur][j][0] + 1.0) * p2 + dp[cur][j - 1][1] * p1; dp[nxt][j][0] = (dp[cur][j - 1][1] + 1.0) * p1 + dp[cur][j][0] * p2; } else { dp[nxt][j][1] = (dp[cur][j][0] + 1 )* p2; dp[nxt][j][0] = dp[cur][j][0] * p1; } } cur = nxt; } printf("Case %d: %.8lf\n",kase++,dp[cur][right][1]); } return 0; }
给一个X * Y * Z 的立方体, 每个单位立方体内都有一盏灯, 初始状态是灭的, 你每次操作如下:
1)选择一个点(x1, y1, z1)
再选择一个点(x2, y2, z2)
将这两个点所形成的立方体内所有的灯全部转换状态(灭的变亮的, 亮的变灭的)
问, K次操作后, 亮着的灯的期望数目。
三维坐标系, 每个维度都是相互独立的, 所以可以分开计算再相乘。
考虑x轴, 对于每个点, 被选中的概率是多少:假设这个点左边有a个点,右边有b个点,
那么这个点不被选中的概率是p = 1.0 - ((x - 1) * (x - 1) - (a * a + b * b)) / x * x。
则,这个点在K次操作后被点亮的期望为:E = sigma C(k, i) * p * (1 - p) ^ (k - i),i为奇数, 因为i为偶数时灯是灭的。
这是二项展开式(p + (1 - p)) ^ k 的所有奇数项。
因此, E = ((p + (1 - p)) ^ k - (-p + (1 - p)) ^ k) / 2, 蓝色部分将所有的奇数项变成了负数, 偶数项不变, 相减后则成了两倍的奇数项之和。
#include <map> #include <set> #include <list> #include <cmath> #include <ctime> #include <deque> #include <stack> #include <queue> #include <cctype> #include <cstdio> #include <string> #include <vector> #include <climits> #include <cstdlib> #include <cstring> #include <iostream> #include <algorithm> #define LL long long #define PI 3.1415926535897932626 using namespace std; int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);} int X,Y,Z,K; double calcu(int x,int n) { return 1.0 - ((x - 1) * (x - 1) * 1.0 + (n - x) * (n - x) * 1.0) * 1.0 / (n * n); } int main() { int T,kase = 1; scanf("%d",&T); while (T--) { double ans = 0.0; scanf("%d%d%d%d",&X,&Y,&Z,&K); for (int i = 1 ; i <= X ; i++) { for (int j = 1 ; j <= Y ; j++) { for (int k = 1 ; k <= Z ; k++) { double p = calcu(i,X) * calcu(j,Y) * calcu(k,Z); ans += (1.0 - pow(1 - 2.0 * p,K)) / 2.0; } } } printf("Case %d: %.8lf\n",kase++,ans); } return 0; }
LightOj 1287 Where to Run
题意:
有n个街口和m条街道, 你后边跟着警察,你需要进行大逃亡(又是大爱的抢银行啊),在每个街口你都有≥1个选择,
1)停留在原地5分钟。
2)如果这个街口可以到xi这个街口, 并且, 通过xi可以遍历完所有未走过的街口,那么就加入选择。
每个选择都是等概率的。
求警察抓住你所用时间的期望, 即你无路可走时的时间期望。
#include <map> #include <set> #include <list> #include <cmath> #include <ctime> #include <deque> #include <stack> #include <queue> #include <cctype> #include <cstdio> #include <string> #include <vector> #include <climits> #include <cstdlib> #include <cstring> #include <iostream> #include <algorithm> #define LL long long #define PI 3.1415926535897932626 using namespace std; int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);} int N,M; const double INF = 1e14; double G[30][30]; double dp[16][1 << 16]; bool vis[16][1 << 16]; bool calcu(int u,int sta) { if (sta == (1 << N) - 1) { dp[u][sta] = 0; return true; } if (vis[u][sta]) return dp[u][sta] > 0; vis[u][sta] = true; dp[u][sta] = 5; int tot = 0; for (int i = 1 ; i <= N ; i++) { if( ((sta & (1 << i)) == 0) && G[u][i] < INF && calcu(i,sta | (1 << i)) ) { int nxtsta = sta | (1 << i); tot++; dp[u][sta] += G[u][i] + dp[i][sta | (1 << i)]; } } if (tot == 0) { dp[u][sta] = 0; return false; } dp[u][sta] /= (double)tot; return true; } int main() { int T,kase = 1; scanf("%d",&T); while (T--) { memset(dp,-1,sizeof(dp)); memset(vis,false,sizeof(vis)); for (int i = 0 ; i < 20 ; i++) for (int j = 0 ; j < 20 ; j++) G[i][j] = INF; scanf("%d%d",&N,&M); for (int i = 0 ; i < M ; i++) { int u,v; double w; scanf("%d%d%lf",&u,&v,&w); G[u][v] = G[v][u] = w; } calcu(0,1); printf("Case %d: %.8lf\n",kase++,dp[0][1]); } return 0; }
LightOJ 1317
有N个人, M个篮框, 每个人投进球的概率是P。
问每个人投K次后, 进球数的期望。
#include <map> #include <set> #include <list> #include <cmath> #include <ctime> #include <deque> #include <stack> #include <queue> #include <cctype> #include <cstdio> #include <string> #include <vector> #include <climits> #include <cstdlib> #include <cstring> #include <iostream> #include <algorithm> #define LL long long #define PI 3.1415926535897932626 using namespace std; int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);} double ans,p; int C[20][20]; int N,K; double calcu(int num) { double ret = 1.0; for (int i = 1 ; i <= num ; i++) ret *= p; for (int i = 1 ; i <= N - num ; i++) ret *= (1 - p); return ret * num * C[N][num]; } int main() { int T,kase = 1; C[0][0] = 1; C[1][0] = C[1][1] = 1; for (int i = 2 ; i < 20 ; i++) { C[i][0] = C[i][i] = 1; for (int j = 1 ; j < i ; j++) C[i][j] = C[i - 1][j] + C[i - 1][j - 1]; } //printf("%d\n",C[8][3]); scanf("%d",&T); while (T--) { scanf("%d%*d%d%lf",&N,&K,&p); double ret = 0.0; for (int i = 0 ; i <= N ; i++) ret += calcu(i); printf("Case %d: %.8lf\n",kase++,ret * K); } return 0; }
LightOj 1321 Sending Packets
题意:
给一个数据大小为S的数据包, 每一次发送需要K秒(单向),现在要从节点0 发送到节点 n-1。
其中有n - 1条路径, 每条路径都有一个传输成功率。
问传输成功所需最小时间的期望。
#include <map> #include <set> #include <list> #include <cmath> #include <ctime> #include <deque> #include <stack> #include <queue> #include <cctype> #include <cstdio> #include <string> #include <vector> #include <climits> #include <cstdlib> #include <cstring> #include <iostream> #include <algorithm> #define LL long long #define PI 3.1415926535897932626 using namespace std; int gcd(int a, int b) {return a % b == 0 ? b : gcd(b, a % b);} const int MAXN = 110; double dp[MAXN][MAXN]; int N,M,S,K; void calcu(int kase) { // memset(dp,0,sizeof(dp)); for (int i = 0 ; i <= N ; i++) dp[i][i] = 1; for (int k = 0 ; k < N ; k++) { for (int i = 0 ; i < N ; i++) { for (int j = 0 ; j < N ; j++) dp[i][j] = max(dp[i][j],dp[i][k] * dp[k][j]); } } double ans = dp[0][N - 1]; ans = 1.0 / ans * 2.0 * K * S; printf("Case %d: %.8lf\n",kase++,ans); } int main() { int T,kase = 1; scanf("%d",&T); while (T--) { scanf("%d%d%d%d",&N,&M,&S,&K); memset(dp,0,sizeof(dp)); for (int i = 0 ; i < M ; i++) { int u,v; double p; scanf("%d%d%lf",&u,&v,&p); dp[u][v] = dp[v][u] = max(dp[u][v],p / 100.0); } calcu(kase++); } return 0; }
LightOj 1342 Aladdin and the Magical Sticks
题意:
地上有n种棍子, 其中有两种类型, 一种类型是可识别, 一种类型是不可识别, 每个棍子都有一个权值。
当你捡到可识别的, 那么你以后就不会再捡这个棍子, 如果是不可识别的, 那么你有可能还会捡。
问将所有棍子收集完的权值的期望。
我们现在面对的是一个n面的骰子, 骰子的每面都是随机出现的(相当于是不可识别的棍子), 求问将所有面都被看完所期望的投掷次数(假设只看最上面那一面)
那么, 问题的解就是:
H[n] = (1 + 1/2 + 1/3 + 1/4 + ... + 1/n), 这就是调和级数的前n项。
这个值近似等于欧拉常数约为:0.57721566490153286060651209。(不过这是一个当n接近无穷时的近似值, 并不能代替具体的H[n], 比如当 n = 1 || 2时)
而所求的是期望的权值, 根据期望的线性性质E(XY) = E(X)*E(Y)
所以, 总的权值期望就等价于 每次的权值期望 * 次数的期望。
n个面, 每个面至少出现一次的期望次数是:E(x) = n * H[n],那么, 某个指定的面至少出现一次的期望次数就是E(z) = E(x)/n = H[n]。
因此, 假设这n个棍子都是不可识别的时候所期望的权值为:
Ea = E(w) * E(x), E(w)为权值的期望 = 权值的平均值。
但是, 这n个棍子里还有一些是可以识别的, 因此还要减去多余的期望。
先来计算一下可识别的棍子所需要的期望的次数, 这个答案为1。
当有六个球在箱子里, 采用不放回抽样, 你将六个球抽出来所期望的次数是多少?这是一个固定的值, 为6。
因此, 每个棍子多出来的部分就是(H[n] - 1) * w[i]。w[i]为某个可识别的棍子的权值。
设, 所有棍子的权值平均值为Wn
假设有k个可识别的棍子, 其权值平均值为Wk
So , 答案为: Ea - Eb = Wn * n * H[n] - k * Wk * (H[n] - 1)
化简: E = (Wn * n - k * Wk) * H[n] + k * Wk。
#include<bits/stdc++.h> using namespace std; const int MAXN = 5010; int n; double h[MAXN]; void init() { h[0] = 0; for(int i = 1; i < MAXN; i ++) h[i] = h[i - 1] + 1.0 / i; } int main() { int T; int kcase = 0; init(); scanf("%d", &T); while(T --) { scanf("%d", &n); int a, b; double ans = 0; for(int i = 0; i < n; i ++) { scanf("%d %d", &a, &b); ans += a * (b == 1? 1 : h[n]); } printf("Case %d: %.5lf\n", ++ kcase, ans); } return 0; }
LightOJ 1364
状压一下混子能充当的牌然后DP
#include<bits/stdc++.h> using namespace std; const int MAXN = 17; const int MAXM = 82; double dp[MAXN][MAXN][MAXN][MAXN][MAXM]; int bit[10]; int C,D,H,S; bool decode(int a,int b,int c,int d,int sta) { memset(bit,0,sizeof(bit)); // printf("%d %d %d %d %d \n",a,b,c,d,sta); int len = 0; while (sta) { bit[len++] = sta % 3; sta /= 3; } a += bit[0]; b += bit[1]; c += bit[2]; d += bit[3]; // printf("%d %d %d %d\n",a,b,c,d); scanf("%*d"); if (a >= C && b >= D && c >= H && d >= S) return true; return false; } double calcu(int a,int b,int c,int d,int sta) { double &ans = dp[a][b][c][d][sta]; if (ans != -1.0) return ans; if (decode(a,b,c,d,sta) == true) return ans = 0.0; int tmp = sta; ans = 0; int len = 0,tot = 0; int bit[10]; memset(bit,0,sizeof(bit)); while (tmp) { bit[len++] = tmp % 3; tmp /= 3; } for (int i = 0 ; i < 4 ; i++) tot += bit[i]; int all = 54 - a - b - c - d - tot; if (a < 13 && all > 0) { double p = (13 - a) * 1.0 / all; ans += (calcu(a + 1,b,c,d,sta) + 1) * p; } if (b < 13 && all > 0) { double p = (13 - b) * 1.0 / all; ans += (calcu(a,b + 1,c,d,sta)+ 1) * p; } if (c < 13 && all > 0) { double p = (13 - c) * 1.0 / all; ans += (calcu(a,b,c + 1,d,sta) + 1) * p; } if (d < 13 && all > 0) { double p = (13 - d) * 1.0 / all; ans += (calcu(a,b,c,d + 1,sta) + 1) * p; } int nxt; double val; if (tot < 2 && all > 0) { double p = (2 - tot) * 1.0 / all; nxt = (bit[0] + 1) + bit[1] * 3 + bit[2] * 9 + bit[3] * 27; val = calcu(a,b,c,d,nxt); nxt = bit[0] + (bit[1] + 1) * 3 + bit[2] * 9 + bit[3] * 27; val = min(val,calcu(a,b,c,d,nxt)); nxt = bit[0] + bit[1] * 3 + (bit[2] + 1) * 9 + bit[3] * 27; val = min(val,calcu(a,b,c,d,nxt)); nxt = bit[0] + bit[1] * 3 + bit[2] * 9 + (bit[3] + 1) * 27; val = min(val,calcu(a,b,c,d,nxt)); ans += p * (val + 1); } return ans; } int main() { int T,kase = 1; scanf("%d",&T); while (T--) { scanf("%d%d%d%d",&C,&D,&H,&S); for (int i = 0 ; i < MAXN ; i++) for (int j = 0 ; j < MAXN ; j++) for (int k = 0 ; k < MAXN ; k++) for (int p = 0 ; p < MAXN ; p++) for (int q = 0 ; q < MAXM ; q++) dp[i][j][k][p][q] = -1.0; int cnt = 0; if (C > 13) cnt += C - 13; if (D > 13) cnt += D - 13; if (H > 13) cnt += H - 13; if (S > 13) cnt += S - 13; if (cnt > 2) printf("Case %d: -1\n",kase++); else printf("Case %d: %.8lf\n",kase++,calcu(0,0,0,0,0)); } return 0; }