zoukankan      html  css  js  c++  java
  • [The 2019 Asia Yinchuan First Round Online Programming] D Take Your Seat

    也许更好的阅读体验

    (mathcal{Description})

    原题链接

    题目大意

    该题目有两个问题

    • (Task 1),有(n)个人(n)个座位,每个人都有一个对应的座位,每个人排队进入去坐座位,小(A)第一个,然而小(A)忘记了自己的位置,于是他进去后自己随便坐了一个位置,之后的人进来后若发现自己的位置已经被人坐了,那么他就随便坐一个没人坐的位置,如果自己的位置没有被人坐,那么就会坐自己的位置,问最后一个进来的人坐到自己的位置概率是多少

    • (Task 2),有(m)个人(m)个座位,该问题与上面问题差不多,但是每个人并不会排队进去,而是随机的进入,即进入的顺序随机的(包括小(A)的进入时间),仍然问最后一个进来的人坐到自己的位置的概率是多少

    复杂度要求在(O(n^3))以内
    实际上只要(O(1))

    (mathcal{Solution})

    这题有点考思维,博主想了很久(博主太蒻了),所以会写一些心得,在引用里面,只想知道题解可以跳过
    主要是考逆向思维,以及一个问题的转化

    不要怕概率这个玩意,尽量想办法避开它,或者只用最简单的概率计算

    考虑递推

    想了很久如何正着推,但是不知道某个人进来时是否可以坐座位,设了很多方程,都是找不到转移或者错误的转移

    一般递推都会是可以将某一个状态变成另一个状态
    所以我们要找到什么样的状态是有联系的

    • (Task 1)

      一直想找到一种方法可以考虑到一个座位是否被坐,博主突然发现了一个性质

      我们知道,当小(A)坐在了第(x)个进来的人的位置,那么第(2)(x-1)个人进来时都是直接坐在自己座位上
      这条性质十分显然也十分简单,可是却很容易将其忽视

      博主忽视了1个多小时...

      另外,小(A)坐在任何一个位置的概率都是(frac{1}{n})
      于是我们可以利用这样的两个性质,开始递推
      我们不认为小(A)是忘记了自己的座位,而是不知道自己的座位

      (f[i])表示有(i)个人时,第一个人不知道自己座位(即第一个人会乱坐)的答案
      当小(A)坐在了自己的座位上时,之后最后一个人坐在自己座位的概率为(100\%)
      当小(A)坐了第(j)个人的座位时,当第(j)个人来找座位时,我们可以认为原本小(A)的座位是他的座位,而他不知道
      因为当他坐在小(A)的座位上时,对后面上来的人都没有影响了
      这样,若小(A)坐了(j)号座位,当(j)来找座位时,相当于是考虑原来的问题,只不过(n)变成了(n-j+1)
      于是我们得到了这样的递推式
      (egin{aligned}f[i]=frac{1}{i}sum_{j=1}^{i-1}f[j]end{aligned})
      初值(f[1]=1)
      上面这个式子可以记一下前缀和然后就可以(O(1))转移了

      然后博主发现从(2)开始,答案都是(frac{1}{2})

    • (Task 2)
      由于是完全随机的顺序,所以没有了上面那个性质

      在想出来(Task 1)的做法后,博主坚信一定是递推,但是又想了一会没想出来
      在内心坚定自己时欧皇的情况下,博主毅然蒙了两个结论提交了程序,于是博主就多了两个罚时...
      因为已经想出来第一问,博主舍不得,于是博主就继续肝了一会儿,又有了一个想法

      仍然是考虑递推,受到(Task 1)的启发,所以仍然按照这个思维模式
      因为是随机顺序,所以小(A)是第几个来坐座位的概率都是(frac{1}{m})
      (f[i])表示有(i)个人的答案
      因为不知道座位是一种特殊的状态,所以我们仍然对它下手
      考虑小(A)是第(j)个来坐座位的人
      这样前(j-1)个人都是坐在自己座位上
      于是现在要考虑的就是有(i-j+1)个人,小(A)第一个来坐座位,剩下的人是随机来坐座位的情况
      (g[i])表示有(i)个人,小(A)第一个来坐座位,剩下的人是随机来坐座位,最后一个人坐到自己座位上的答案
      然后我们枚举小(A)是第几个
      得到这样的递推式
      (egin{aligned}f[i]=frac{1}{i}sum_{j=1}^{i}g[i]end{aligned})
      于是现在就是要想办法求(g)了,终于把问题转化为一个感jio简单些的问题了

      考虑求(g[i]),我们仍然按照上面的思维模式,考虑小(A)坐在了第(j)个来选座位的人的座位
      因为是随机顺序,所以小(A)随便坐一个位置,其是第(j)(j)任取)个上来的人的座位的概率仍然是(frac{1}{i})
      于是,在(j)前面的人都坐自己的位置,从(j)开始,又是一个(i-j+1)个人的问题,当然,别忘了坐在自己坐
      所以得到(g)的递推式
      (egin{aligned}g[i]=frac{1}{i}sum_{j=1}^{i-1}g[i]end{aligned})

      诶,这个和(Task 1)(f)是一样的.....

      于是问题就得到了解决

      真是可喜可贺可喜可贺...

    噢,这还只是(O(n))是吧
    然后我们经过打表发现
    对于(Task 1)
    (f[1]=1,f[n]=0.5(n>1))
    然后对于(Task 2)
    (g[m]=frac{m+1}{2m})

    (mathcal{Code})

    这里仍然给了(f,g)的递推(以免有不太明白的地方),但是直接用上面的结论(O(1))输出的

    /*******************************
    Author:Morning_Glory
    LANG:C++
    Created Time:2019年08月31日 星期六 15时45分43秒
    *******************************/
    #include <cstdio>
    #include <fstream>
    using namespace std;
    const int maxn = 55;
    const int mx = 50;
    int T,n,m;
    double sum;
    double f[maxn],g[maxn];
    void init ()
    {
    	sum=g[1]=f[1]=1,f[2]=1.0/2.0;
    	for (int i=2;i<=mx;++i)	f[i]=1.0/i*sum,sum+=f[i],g[i]=sum/(double)i;
    }
    int main()
    {
    	scanf("%d",&T);
    	init();
    	for (int t=1;t<=T;++t){
    		scanf("%d%d",&n,&m);
    		printf("Case #%d: %.6lf %.6lf
    ",t,n>1?0.5:1,(double)(m+1)/(double)(2.0*m));
    	}
    	return 0;
    }
    
    

    如有哪里讲得不是很明白或是有错误,欢迎指正
    如您喜欢的话不妨点个赞收藏一下吧

  • 相关阅读:
    UNIX网络编程读书笔记:辅助数据
    使用Android Studio搭建Android集成开发环境(图文教程)
    Android数据存储(二)----PreferenceFragment详解
    Android系列----JUnit单元测试的使用
    Android数据存储(一)----SharedPreferences详解
    使用IntelliJ IDEA 13搭建Android集成开发环境(图文教程)
    Android系列之网络(三)----使用HttpClient发送HTTP请求(分别通过GET和POST方法发送数据)
    Android系列之网络(二)----HTTP请求头与响应头
    Android系列之网络(一)----使用HttpClient发送HTTP请求(通过get方法获取数据)
    Android多线程----异步消息处理机制之Handler详解
  • 原文地址:https://www.cnblogs.com/Morning-Glory/p/11442231.html
Copyright © 2011-2022 走看看