zoukankan      html  css  js  c++  java
  • CF605E Intergalaxy Trips 贪心 概率期望

    (当时写这篇题解的时候,,,不知道为什么,,,写的非常冗杂,,,不想改了。。。)

    题意:一张有n个点的图,其中每天第i个点到第j个点的边都有$P_{i, j}$的概率开放,每天可以选择走一步或者留在原地,求从1号点到n号点的最优期望值。
    题解:
      $f(x)$表示从$x$出发,走到$n$的最优期望时间。因为在一个点x时,如果要选择后继节点,肯定要选$f$值越小的越好,因此考虑贪心的选择后继状态。$a_{i}$表示$f(x)$第$i$小的x。考虑分层DP,依次确定$a_{i}$的值,并同时维护$f$值。
      假设我们现在枚举到了第$i$层,那么对于任意一点$x$,只在$a_{1}...a_{i - 1} + x$里面选择后继状态,因为只有这样才能保证期望不变得更劣(如果要选择其他点作为后继的话,可以看做等到了那个点已经被加入$a$集合中再考虑作为后继,如果那个点加入$a$集合的时间还要比$x$慢,那肯定去那个点是会使得期望变大的,还不如不去)
      注意到如果一个点$x$已经被放入$a$集合,那么肯定不需要再更新它了,因为它的值已经求出来并且固定了。(其实感觉思路有点类似于dijkstra,都是用已经找到最短路的点来更新其他点,每多找到一个点的最优解,下次就多用这个点来更新其他点)
      对于第$i$层的最优点$a_{i}$,我们有:
      $$f(a_{i}) = 1 + sum_{j = 1}^{i}(f(a_{j}) P_{a_{i}, a_{j}} prod_{k = 1}^{j - 1}(1 - P_{a_{i},a_{k}}))$$
      当然这个式子对于其他点$x$也是成立的,只需要把所有的$a_{i}$都替换成$x$即可,注意要把$j == i$时的$a_{j}$也替换掉。于是替换完后再移下项,把$f(x)$都移到左边,式子就变为了:
      $$[1 - prod_{k = 1}^{i - 1}(1 - P_{a_{i}, a_{k}})] f(x) = 1 + sum_{j = 1}^{i - 1}(f(a_{j}) P_{x, a_{j}} prod_{k = 1}^{j - 1}(1 - P_{x, a_{k}}))$$
      应该还是比较好理解的,因为只会选择$a$集合中的点,且在$a$数列中越靠前的越好,因此要优先考虑选靠前的$a_{j}$,对于一个$a_{j}$,只有$a_{1} ... a_{j - 1}$都走不到的时候才会选择它,因此它的贡献就是可以到它的概率*之前的点都不能到的概率。
      如果所有$a$集合中的点都到不了,我们还有保底的选项——留在原地,这个选择体现在第一个式子中的求和符号的上界为$a_{i}$自己,而在第二个式子中,这一部分已经被移项到了左侧。
      因此我们只需要按层DP,在每层内,选择$f$值最小的那个作为新点加入$a$集合,不断更新维护其他未确定点的$f$即可。、
      
      那么我们考虑怎么来实现这个东西。。。(实现细节)
      观察到式子中,$i$是从小到大枚举的,也就是说对于相邻的两层$i - 1, i$中的同一个点$x$,它们的$f(x)$所相差的仅仅只有式子中随着$i$增大而多出来的那部分。因此我们可以每次只算出增量,然后每次都与前面的累加,得到新的$f$值,这样就可以快速的算出$f$值。
      但是如果我们直接求$f$值,也就是把左边那坨系数除到右边去的话,就不太好累加,因此我们考虑将右边和左边分别储存,用$f$数组来存右边的值,$g$来存左边的系数,这样如果我们需要用到$f$的值,直接用$g$和$f$凑出真正的$f$值即可。
      但是考虑到等式右边有一个部分和等式左边是一样的:$prod_{k = 1}^{j - 1}(1 - P_{x, a_{k}})$,因此如果我们令$g$表示这个,而不是左边那一整坨的话,就可以快速的得到左边需要的值。这样的话,要表示真正的$f$值只需要用$f$去除$(1 - g)$即可。
      方便起见,如果一个点$pos$即将被加入$a$集合,我们就把这个点真实的$f$值计算出来,以供后面的点使用。
      观察到对于每一层的$f$值,我们实际上只用到了$1...i - 1$层的值,因此我们在循环的开头就已经可以计算出$f$的大小了,所以我们只需要在循环的开头就把当前层要加入$a$集合的那个点,也就是把$f$值最小的那个点加入集合。
      然后再对于所有没有被加入$a$集合的点,处理下一层所需要的$f$值即可。
      因为下一次的$i - 1$相当于现在的$i$,而加入现在的$i$时,所需要用到的$g$值还停留在现在的$i - 1$,所以要先更新$f$,再更新$g$.
      更具体的来说,就是:
      对于$f(x)$,每次新增$f(a_{i})P_{x, a_{i}} prod_{k = 1}^{i - 1}(1 - P_{x, a_{k}}) = f(a_{i})P_{x, a_{i}}g(x)$.
      对于$g(x)$,每次新乘$(1 - P_{x, a_{i}})$

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define R register int
     4 #define AC 1100 
     5 #define db double
     6 
     7 int n, p[AC][AC];
     8 double f[AC], g[AC];
     9 bool z[AC];
    10 
    11 inline int read()
    12 {
    13     int x = 0;char c = getchar();
    14     while(c > '9' || c < '0') c = getchar();
    15     while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    16     return x;
    17 }
    18 
    19 void pre()
    20 {
    21     n = read();
    22     for(R i = 1; i <= n; i ++)
    23         for(R j = 1; j <= n; j ++) p[i][j] = read();    
    24     for(R i = 1; i < n; i ++) f[i] = g[i] = 1;//f[n], g[n] = 0
    25 }
    26 
    27 void work()
    28 {
    29     for(R i = 1; i <= n; i ++)//枚举层数
    30     {//因为已经存下了前缀和,所以就不需要存a_i了
    31         double maxn = 1e18; int pos = 0;
    32         for(R j = 1; j <= n; j ++) 
    33             if(!z[j] && g[j] < 1 && f[j] / (1 - g[j]) < maxn) 
    34                 maxn = f[j] / (1 - g[j]), pos = j; 
    35         if(!pos) break;//因为pos的f已经被确定下来了,所以这时就可以把(1 - g[pos])除过去了
    36         z[pos] = 1, f[pos] /= (1 - g[pos]);
    37         for(R j = 1; j <= n; j ++)
    38             if(!z[j])
    39             {
    40                 f[j] += g[j] * f[pos] * ((db) p[j][pos] / 100);
    41                 g[j] *= (db) (100 - p[j][pos]) / 100;
    42             }
    43     }
    44     printf("%.10lf", f[1]);
    45 }
    46 
    47 int main()
    48 {
    49 //    freopen("in.in", "r", stdin);
    50     pre();
    51     work();
    52 //    fclose(stdin);
    53     return 0;
    54 }
    View Code
  • 相关阅读:
    如何用Core Plot绘制柱状图
    如何定制Core Plot主题
    开源框架Core Plot的使用
    基于.NET平台的分层架构实战(七)——数据访问层的第一种实现:Access+SQL
    基于.NET平台的分层架构实战(十一)——表示层的实现
    CIO如何给自己的团队带来价值
    如何使用Core Plot绘制直方图和折线图
    在Windows平台下编译ObjectiveC(转载)
    新开通博客
    Java类中的对象
  • 原文地址:https://www.cnblogs.com/ww3113306/p/10206536.html
Copyright © 2011-2022 走看看