zoukankan      html  css  js  c++  java
  • csp-s模拟测试101的T3代码+注释

    因为题目过于大神所以单独拿出来说。而且既然下发std了颓代码貌似也不算可耻233

    很难讲啊,所以还是写在代码注释里面吧

    因为比较认真的写了不少注释,所以建议缩放到80%观看,或者拿到gedit上

     1 //不要在意我写的是“气”,输入法找不到那个字。。。
     2 #include<cstdio>
     3 #include<algorithm>
     4 long double C[205][205];int n,g,t,a[205],N;
     5 struct Ex{//用于存储期望值
     6     long double cnt,tot;//cnt表示到达这个状态的总方案数,tot表示这些方案所获得的神的总和
     7     void add(long double e,long double c){tot+=e*c;cnt+=c;}//这里是正推的,所以概率加和的结果不为1,所以期望不能直接相加
     8     //期望其实类似于加权平均数。现在你有cnt个方案,和为tot。要加入c个方案,这些方案的平均数是e,所以这c个方案的和为e*c
     9     //那么总的方案数就是cnt+c。总的和就是tot+e*c
    10     long double E(){return cnt?tot/cnt:0;}//期望值就是总量/方案数啊,可以重载成+=,但是因为参数有2个还得写pair什么的所以就没有重载
    11 }w[205][205],f[205][205];//全局变量初值为0,所以刚开始的方案数和期望值都是0
    12 long double cal(int l,int r,int ord){return ord>n?0:(l+r+1)/2.0;}//计算平均数,表示用了一份l的气所得到的神的期望。
    13 //如果用的是原来不存在的气,就是你加的那些编号为[n+1,n+t]的气,这些气是不存在的,所以其实你放弃了这一份精,所以并不会得到神,返回0
    14 int main(){
    15     freopen("surmount.in","r",stdin);freopen("surmount.out","w",stdout);
    16     scanf("%d%d%d",&n,&g,&t);//方便查阅:n是气的份数,g是单次最大的精量,t是轮数
    17     for(int i=1;i<=n;++i)scanf("%d",&a[i]);
    18     for(int i=1;i<=n;++i)if(a[i]>g)a[i]=g;//大于g的气是没有用的,直接重置为g
    19     std::sort(a+1,a+1+n);N=n+t;//排序方便处理连续的区间
    20     for(int i=n+1;i<=N;++i)a[i]=g;//添加一些足够大的气,这样在下面处理的时候就能保证每一份精都能被压制
    21     for(int i=0;i<=t;++i)C[i][0]=1;
    22     for(int i=1;i<=t;++i)for(int j=1;j<=i;++j)C[i][j]=C[i-1][j-1]+C[i-1][j];//处理组合数
    23     //w[i][j]表示对于[i,j)这段区间的所有气,如果使用了它们所获得的神的期望值
    24     for(int i=0;i<=N;++i)w[i][i].add(0,1);//赋初值:空区间的方案数是1,对神的贡献为0
    25     for(int i=N;~i;--i)for(int j=i+1;j<=N&&j-i<=t;++j)for(int k=i;k<j;++k)
    26         w[i][j].add(w[i][k].E()+w[k+1][j].E()+cal(a[k+1],a[i],k+1),w[i][k].cnt*w[k+1][j].cnt*(a[k+1]-a[i])*C[j-i-1][k-i]);
    27     //现在的操作是要把[i,k)与[k+1,j)合并成[i,j)。假设最后一个使用的精是k。注意区间开闭。
    28     //add的两个参数:期望值就是[i,k)和[k+1,j)这两段的期望值的和,然而还有使用k所得到的神,你使用它是用来压制[a[k+1],a[i]]的精,得到对应的神,详见cal函数
    29     //总方案数就是两边的方案相乘(乘法计数原理)。还要乘上组合数,因为要考虑这些气的顺序可以颠倒。因为左右两边内部已经有序了,所以只需要把两个混合起来。
    30     //就是类似于归并排序两个内部有序的数组,总方案数就是C[j-i-1][k-i],表示在所有j-i-1个位置里选出左边所用的k-i个元素的位置,注意第k个被你强制定为最后一个了所以是j-i还要-1
    31     for(int i=0;i<=t;++i)f[i][i]=w[0][i];
    32     //初值,表示前i-1份气没有用。超过t没有意义。
    33     for(int i=0;i<=N;++i)for(int k=0;k<=t;++k)if(f[i][k].cnt)for(int j=i+1;j<=N&&k+j-i-1<=t;++j)
    34         f[j][k+j-i-1].add(f[i][k].E()+w[i+1][j].E(),f[i][k].cnt*w[i+1][j].cnt*C[k+j-i-1][k]);
    35     //i表示转移之前已经用了几份气(含),j表示转移之后用的气,k表示转移了几轮,所以k+j-i-1就是转移之后已经经过了的轮数
    36     //f[a][b]表示用了前a份气,已经经过了b轮之后得到的神的期望,所以最后答案是f[N][t]
    37     //转移就是,先看期望,就是已经做完的f[i][k]再加上新的一轮你用了[i+1,j)这一段的气,这样所获得的神
    38     //总方案数其实也一样,和w的转移比较类似,也是排序了两个内部有序的过程
    39     printf("%.10Lf
    ",f[N][t].E());
    40 }//By DeepinC究级压行+垃圾注释(硬核25行)

    累。。。

  • 相关阅读:
    ACM-ICPC 2018 南京赛区网络预赛 J.Sum
    汉诺塔
    汉诺塔
    D
    D
    数学小定理
    数学小定理
    Python index()方法
    Python endswith()方法
    Python encode()方法
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/11804141.html
Copyright © 2011-2022 走看看