zoukankan      html  css  js  c++  java
  • [BZOJ4899]记忆的轮廓

    记忆的轮廓

    题目描述

    通往贤者之塔的路上,有许多的危机。
    我们可以把这个地形看做是一颗树,根节点编号为1,目标节点编号为n,其中1-n的简单路径上,编号依次递增,在[1,n]中,一共有n个节点。我们把编号在[1,n]的叫做正确节点,[n+1,m]的叫做错误节点。一个叶子,如果是正确节点则为正确叶子,否则称为错误叶子。莎缇拉要帮助昴到达贤者之塔,因此现在面临着存档位置设定的问题。
    为了让昴成长为英雄,因此一共只有p次存档的机会,其中1和n必须存档。被莎缇拉设置为要存档的节点称为存档位置。当然不能让昴陷入死循环,所以存档只能在正确节点上进行,而且同一个节点不能存多次档。因为通往贤者之塔的路上有影响的瘴气,因此莎缇拉假设昴每次位于树上一个节点时,都会等概率选择一个儿子走下去。每当走到一个错误叶子时,再走一步就会读档。具体的,每次昴到达一个新的存档位置,存档点便会更新为这个位置(假如现在的存档点是i,现在走到了一个存档位置j>i,那么存档点便会更新为j)。读档的意思就是回到当前存档点。初始昴位于1,当昴走到正确节点n时,便结束了路程。莎缇拉想知道,最优情况下,昴结束路程的期望步数是多少?

    输入格式

    第一行一个正整数T表示数据组数。
    接下来每组数据,首先读入三个正整数n,m,p。
    接下来m-n行,描述树上所有的非正确边(正确边即连接两个正确节点的边)
    用两个正整数j,k表示j与k之间有一条连边,j和k可以均为错误节点,也可以一个为正确节点另一个为错误节点。
    数据保证j是k的父亲。
    50<=p<=n<=700,m<=1500,T<=5。
    数据保证每个正确节点均有至少2个儿子,至多3个儿子。

    输出格式

    T行每行一个实数表示每组数据的答案。请保留四位小数。

    样例

    样例输入

    1
    3 7 2
    1 4
    2 5
    3 6
    3 7
    

    样例输出

    9.0000
    

    数学是OI生涯难以跨过的一道坎,概率期望是数学这道坎上的一个大坑,看见概率期望就懵

    这道题我真的膜拜出题人,因为他的数据里其实有50%是n==p(然而我们的题面和BZOJ上都没有写,其实写了我也不一定能拿50),先搞定这50%吧

    50%

    $n==p$也就是每个点都可以存档,那可以先求出每个错误叶子期望走多少步可以读档,由当前错误叶子的错误儿子推来,儿子的期望乘以概率$frac{1}{du[i]}$为可由此儿子转移而来的期望步数,由儿子还需再走一步才可到当前错误叶子

    $g[i]=sumlimits_{j为i的儿子}frac{g[j]+1}{du[i]}$

    我们设$f[i]$表示由正确叶子i到n所要的期望步数,倒着推,那$f[n]=0$

    $f[i]=frac{f[i+1]+1}{du[i]}+sumlimits_{j是i的儿子}frac{g[j]+f[i]+1}{du[i]}$

    此处$j$为$i$的错误儿子,我们把$∑g[i]$提前预处理出来,存到$sum[i]$就避免了一层循环,通分移项化简得

    $f[i]=du[i]+f[i+1]+sum[i]$

    最后的$f[1]$就是要的结果

    70%

    50分搞定了就要想办法解决$n≠p$的情况了,那我们就用$f[i][j]$记录到点$i$还剩$j$次存档机会到$n$的期望步数,先处理出$a[i][j]$$i$为存档点到正确叶子$j$的期望步数,你肯定是不断的向后走,所以$j>i$,预处理是$a[i][i]=0$,进行递推

    $a[i][j]=a[i][j-1]+sumlimits_{k是j的错误儿子}frac{g[k]+a[i][j]+1}{du[j]}$

    还是移项

    $a[i][j]=du[j]*a[i][j-1]+du[j]+sum[j]$

    $f[i][j]=min(f[i][j],f[k][j-1]+a[i][k])$

    $k$为可转移的点,$f[1][p]$就是结果

    不过这个70%的程序到我手里50TLE,明明有跟我写法一样70TLE的,我也不知道我为啥就50,然后就去看了出题人的优化,他说a最大是$2^{40}$,最多转移40步以内的就够用了,咱也没看太懂,咱也没地问,咱就是个OI蒟蒻,然后我就限制了这个转移步数,然后我就用70%的题解A掉了这道题,100%的那个算法我大概看了一眼,要单队优化,要二分,蒟蒻伤不起啊

    以下是摘自出题人博客关于最多40步之内转移的解释,不过我最开始看一个同学的题解,15步就足够A掉这道题的,可能大概是步数多了之后的期望步数全被舍掉了,没有被利用的价值吧,毕竟要求的是最优解

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #define maxm 1600
     5 #define pp 40
     6 using namespace std;
     7 int t,js,n,m,p;
     8 int head[maxm],to[maxm],xia[maxm],son[maxm];
     9 double cwqw[maxm],sum[maxm];
    10 double a[maxm][maxm],f[maxm][maxm];
    11 bool pd[maxm];
    12 int read()
    13 {
    14     int e=0,f=1;  char ch=getchar();
    15     while(ch<'0'||ch>'9')
    16     {
    17         if(ch=='-')  f=-1;
    18         ch=getchar();
    19     }
    20     while(ch>='0'&&ch<='9')  {e=(e<<3)+(e<<1)+(ch^48);  ch=getchar();}
    21     return e*f;
    22 }
    23 void add(int x,int y) 
    24 {
    25     to[++js]=y;
    26     xia[js]=head[x];
    27     head[x]=js;
    28 }
    29 void dfs(int x)
    30 {
    31     pd[x]=1;  cwqw[x]=1.0000;
    32     for(int i=head[x];i;i=xia[i])
    33     {
    34         int ls=to[i];  dfs(ls);
    35         cwqw[x]+=cwqw[ls]/(double)son[x];
    36     }
    37 }
    38 int main()
    39 {
    40     t=read();
    41     while(t--)
    42     {    
    43         n=read();  m=read();  p=read();  js=0;
    44         for(int i=1;i<=m;++i)  head[i]=to[i]=xia[i]=son[i]=sum[i]=pd[i]=0;
    45         for(int i=1;i<=m-n;++i)  {int j=read(),k=read();  add(j,k);  son[j]++;}
    46         for(int i=n+1;i<=m;++i)
    47             if(pd[i]==0)  dfs(i);
    48         for(int i=1;i<=n;++i)
    49             for(int j=head[i];j;j=xia[j])  sum[i]+=cwqw[to[j]];
    50         for(int i=1;i<=n;++i)
    51         {
    52             a[i][i]=0;
    53             for(int j=i+1;j<=n;++j)
    54                 a[i][j]=(double)((double)son[j-1]+1)*a[i][j-1]+(double)son[j-1]+sum[j-1]+1;
    55         }
    56         memset(f,127,sizeof(f));  f[n][1]=0;
    57         for(int j=2;j<=p;++j)
    58             for(int i=1;i<=n;++i)
    59                 for(int k=i+1;k<=n&&k-i<=pp;++k)  f[i][j]=min(f[i][j],f[k][j-1]+a[i][k]);
    60         printf("%0.4lf
    ",f[1][p]);
    61     }
    62     return 0;
    63 }
    View Code
  • 相关阅读:
    全网最贴心webpack系列教程和配套代码
    webpack4 系列教程(十五):开发模式与webpack-dev-server
    CSS元素显示模式
    CSS符合选择器
    Emmet语法
    CSS引入方式
    CSS文本属性
    CSS字体属性
    CSS语法规范一
    案例
  • 原文地址:https://www.cnblogs.com/hzjuruo/p/11295990.html
Copyright © 2011-2022 走看看