• USACO 2018 December Contest Platinum T1: Balance Beam


    题目大意

    Bessie为了存钱给她的牛棚新建一间隔间,开始在当地的马戏团里表演,通过在平衡木上小心地来回走动来展示她卓越的平衡能力。

    Bessie能够通过表演赚到的钱取决于她最终成功跳下平衡木的位置。平衡木上从左向右的位置记为 0,1,,N+1 。(2N1e5) 如果Bessie到达了位置 0 或是 N+1 ,她就会从平衡木的一端掉下去,遗憾地得不到报酬。

    如果Bessie处在一个给定的位置 k ,她可以进行下面两项中的任意一项:

    1. 投掷一枚硬币。如果背面朝上,她前往位置 k1 ,如果正面朝上,她前往位置 k+1 (也就是说,每种可能性 1/2 的概率)。

    2. 跳下平衡木,获得 f(k) 的报酬(0f(k)1e9 )。

    Bessie意识到她并不能保证结果能够得到某一特定数量的报酬,这是由于她的移动是由随机的掷硬币结果控制。然而,基于她的起始位置,她想要求出当她进行一系列最优的决定之后,她能够得到的期望报酬(“最优”指的是这些决定能够带来最高可能的期望报酬)。

    例如,如果她的策略能够使她以 1/2 的概率获得 10 的报酬,1/4 的概率获得 8 的报酬,1/4 的概率获得 0 的报酬,那么她的期望报酬为加权平均值 10 * (1/2)+8 * (1/4)+0 * (1/4)=7 。

    题目分析

    对于给定的一个点我们要考虑的就是 跳下所得收益 和 移动后的收益期望,其中 跳下所得收益 已知,考虑如何求出 移动后的收益期望。

    观察

    显然,如果我们已知 某些节点x(令x∈A) 移动的期望收益比 它们的 停止收益低,即如果在 另一些点 进行移动操作,一旦移动到A中的点,最优的策略就是不再移动,称A中点为停止点。

    如果从 点i 出发进行移动,那么移动的期望收益一定是由 i 前面第一个停止点和后面第一个停止点贡献的。

    更具体地,设这两个停止点为 a, b, (a < i < b) 那么该 点i 的移动收益 Ei va * (b-i) / (b-a) + vb*(i-a) / (b-a)    (va, vb分别为a, b点跳下所得收益)

    怎么得来的呢?

    考虑设在 i 开始,到 停止的概率为 Fi,由题可得 F= (Fi-1 + Fi+1)/ 2,不难发现这个式子是等差数列的描述,又因为 F0=0, FL=1 可得 Fi​ = i/L

    同样地,可以得到在 i 开始,到 停止的概率为 Gi = (L - i)/ L

    推广,对于上面的三个点 a, i, b  (a < i < b), Ei va * (b-i) / (b-a) + vb*(i-a) / (b-a)

    至此,我们仅需考虑如何找出所有停止点

    不难发现,若将点A (a, va),B(b, vb)在平面直角坐标系中画出,Ei 的取值即为 直线x=i 与 线段AB交点。(可用梯形面积证明)

    若 vi > Ei, 则显然 点i 为一个停止点。

    根据以上过程,明显可以看出 所有停止点构成了一个凸包(一个完整凸包的上半部分,也就像一个上半圆弧)

    至此,只要找出所有点 (i, vi)构成的凸包,然后递推从前向后扫一遍,即可得出答案。

     1 #include<bits/stdc++.h>
     2 typedef long long ll;
     3 const int MAXN=1e5+10;
     4 
     5 int n,top;
     6 ll bse=1e5;
     7 struct Node{
     8     int x;
     9     ll y;
    10     friend Node operator -(const Node &a,const Node &b){return (Node){a.x-b.x,a.y-b.y};}
    11     friend ll operator *(const Node &a,const Node &b){return a.x*b.y-a.y*b.x;}
    12 }a,sta[MAXN];
    13 
    14 inline void Push_back(Node a){
    15     while(top&&(a-sta[top])*(sta[top]-sta[top-1])<=0) --top;
    16     sta[++top]=a;
    17 }
    18 int main(){
    19     scanf("%d",&n);
    20     for(int i=1;i<=n;++i){
    21         a.x=i;
    22         scanf("%lld",&a.y);
    23         a.y*=bse;
    24         Push_back(a);
    25     }
    26     Push_back((Node){n+1,0});
    27     for(int i=1,j=0;i<=n;++i){
    28         while(j<top&&sta[j].x<i) ++j;
    29         if(sta[j].x==i)
    30             printf("%lld
    ",sta[j].y);
    31         else
    32             printf("%lld
    ",((sta[j].x-i)*sta[j-1].y+(i-sta[j-1].x)*sta[j].y)/(sta[j].x-sta[j-1].x));
    33     }
    34     return 0;
    35 } 

     

  • 相关阅读:
    Android防止按钮连续点击
    Android中的AlertDialog遇到的错误
    android通过Jni加载so库遇到UnsatisfiedLinkError问题!!!
    接口回调
    Android中的APinner2
    AndroidAPI
    Android中的下拉列表
    学习地址
    2018/12/21:Date类
    2018/12.21:找出数组最大项和最小项。
  • 原文地址:https://www.cnblogs.com/LI-dox/p/11216673.html
Copyright © 2020-2023  润新知