1 条题解

  • 0
    @ 2025-8-24 21:33:31

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar 学委
    希望以后某时候还能来写题!

    搬运于2025-08-24 21:33:31,当前版本为作者最后更新于2018-08-30 17:11:43,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    2018-10-24 更新

    思路

    与先前写题解的大佬们略有不同,我觉得可能更好理解一些。

    如果有情况A有 mm 的概率发生,且它有 nn 的概率直接转换情况B,那么情况B发生的概率可加上 m×nm×n

    概率可以层层(一层指的是淘汰一次)继承,可以考虑用DP。

    这道题直觉是顺序递推,第一个人有 1m\frac{1}{m} 的几率选择某一张牌,所以下一个状态出现的概率为 1m\frac{1}{m}。但是下一个状态很古怪,因为这个状态除了包括概率值,还包括“谁被淘汰了”,后者也会影响接下来的淘汰结果。仔细考虑发现不好实现。

    最好有一种继承方式不需要保留“谁被淘汰”这个状态。那就是倒序递推,从环内人数只有1的状态开始。


    模拟一下样例1。现在卡牌是给定了,上面写着2,3,5,7,11。


    当环内只有一个人时(没人钦定这是谁),这个人已经赢了,赋胜率100%。


    考虑环内有两个人的情况(这也是不指定的两人)。这时的庄家可以从5张牌中任意选一个,每张牌被选中的概率都是 15\frac{1}{5}

    如果选2。淘汰对方,自己胜利。

    如果选3,5,7,11中的一张牌,可算出他淘汰了自己,留下了第二个人。

    可见,余留两个人时,庄家胜率20%,坐在他后面的人胜率80%。

    暂时看不出什么端倪。其实,“还剩两人”的状态对于解决“还剩三人”特别有利。


    考虑环内有三个人的情况。

    庄家 15\frac{1}{5} 几率选2。结果是第2个人遭到了淘汰,剩下的3,1两人就组成了标准的两人环的情况!

    若庄家选择牌2,则2号被淘汰(不会得到胜率贡献),3号(成为两人环的庄主)的胜率就将是20%,1号(成为两人环的小二)的胜率就将是80%。

    当然这样分配胜率的前提是“本轮中庄家确实选了牌2”,实际概率仅 15\frac{1}{5};所以对3号胜利的真正贡献是 15×20%\frac{1}{5} × 20\%,对1号胜利的真正贡献是 15×80%\frac{1}{5} × 80\%

    庄家还可能选择别的牌(还可能淘汰自己)。但是淘汰掉三人中的一个后,接下来的两人形成新环(庄主可能改变),所以可以直接从两人环的情况继承。

    这种方法不用保留谁被淘汰掉的状态。


    四个人成环的情况,同样枚举庄家选择了什么牌,被淘汰的人之后的三个人的情况就可以利用已求出的三人环了。

    //在第四层内,我们将根据已求出的三人环求出四人环内每个人的胜利几率。
    //下面所有“4”是第四层的的意思,仅供模拟当前层。a[]数组存储着牌上的数字
    
      for(int k = 1; k <= m; ++k)//枚举4人中的庄家选的牌
      {
          int p = (a[k] % 4 == 0) ? 4 : a[k] % 4;
          //如果取a[k]这张牌,淘汰掉p(p = a[k] % 4),那么下一轮p+1坐庄,p+2将是二号
          //因此p+1获得“下一盘庄主的胜率 × 1/m”,p+2获得“下一盘小二的胜率 × 1/m”...
    
          for(int j = 1; j <= 4-1; ++j)//被淘汰者的后面的3个人,就成为新3人环的第1、2、3个人,因此可以获得一些胜率
          {
              ++p;//后面一个人
              if(p > 4)//这是回到环首了
                  p = 1;				
              f[4][p] += f[4-1][j] / (double)(m);//根据上一层状态继承。本层第p个对应上一层第j个。注意这一选择的实际发生几率只有1/m
          }
      }
    

    代码

    //头文件等没特别
    int n, m;
    int a[51];//存各个牌上的数字
    
    double f[51][51];
    //对于指定的的卡牌,f[i][j]指的是i人形成的环中,第j个人的胜率
    //我们将倒序递推出f数组
    
    int main()
    {
    	n = getint(), m = getint();
    	for(int i = 1; i <= m; ++i)
    		a[i] = getint();
    	
    	f[1][1] = 1.0;//只剩一个人时,此人100%获胜
     
    	for(int i = 2; i <= n; ++i)//根据i-1人环,求出i人环的情况
    		for(int k = 1; k <= m; ++k)//枚举本轮庄家选择的牌
    		{
    			int p = (a[k] % i == 0) ? i : a[k] % i;
    			//如果取a[k],第a[k] % i(用p记录)人被淘汰,下一轮p+1坐庄
                
    			for(int j = 1; j <= i-1; ++j)
    			{
    				++p;//下一个人
    				if(p > i)
    					p = 1;				
    				f[i][p] += f[i-1][j] / (double)(m);
    			}
    		}
    			
    	for(int i = 1; i <= n; ++i)
    		printf("%.2lf%% ", f[n][i] * 100.0);
    	return 0;
    }
    
    • 1

    信息

    ID
    1024
    时间
    1000ms
    内存
    125MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者