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;
}
View Code

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;
}
View Code

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;
}
View Code

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;
}
View Code

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);
    }
}
View Code

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;
}
View Code

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;
}
View Code

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;
}
View Code

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;
}
View Code

 给一个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;
}
View Code

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;
}
View Code

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;
}
View Code

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;
}
View Code

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;
}
View Code

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;
}
View Code

 

posted @ 2016-03-24 21:07  Commence  阅读(529)  评论(0编辑  收藏  举报