P1409题解

P1409 题解

博客里食用更佳。

题意

题目的意思浅显易懂:

\(n\) 个人站成一排,小明(临时演员)是第 \(m\) 个人,每轮上帝会投一次筛子,分为以下情况:

  • 若掷到 \(1\),则队首的人获胜;
  • 若掷到 \(2\)\(4\)\(6\),则队首的人出队;
  • 若掷到 \(3\)\(5\),则队首的人排到队尾。

问:小明有多少的胜率?(注意:输出保留九位小数)

思路

这不就是一道 dp 和数学结合起来的题吗???

分析

我们就设二维数组 \(f[1005][1005]\)\(f[i][j]\) 表示有 \(i\) 个人,小明在第 \(j\) 个位置,小明的胜率。

易知,\(f[1][1]=1\),因为只有一个人的时候小明直接胜利!且由题意得 \(j\leqslant i\)

接下来,我们需要把题目带进来,求出状态转移方程式(动态规划的递推式)。

  1. 掷到 \(1\),则队首的人获胜,这时候,小明有 \(\frac{1}{6}\) 的概率会输掉(对手的人胜利了,小明就输了)。
  2. 掷到 \(2\)\(4\)\(6\),则队首的人出队,此时,队首的人有 \(\frac{1}{2}\) 的概率会转回队尾,(那又是一个天道好轮回了……)也就是相当于小明又往前走了一步(\(j-1\)),而总人数(\(i\))不变,表示为 \(f[i][j-1]\)
  3. 掷到 \(3\)\(5\),则队首的人排到队尾,此时,队首的人有 \(\frac{1}{3}\) 的概率出队,也就是相当于小明又往前走了一步(\(j-1\)),而总人数却又少了一个人(\(i-1\)),表示为 \(f[i-1][j-1]\)

列出了上表,dp递推式也就很简单了,由于小明为主角,绝不能输,所以就不讨论第一种情况,所以,对于 \(x\)\(y\)\(x\leqslant y\)\(x\)\(y\) 为正整数),都有 \(f[x][y]\),此时有 \(\frac{1}{2}\) 的概率跳到 \(f[x][y-1]\);有 \(\frac{1}{3}\) 的概率跳到 \(f[x-1][y-1]\) 所以推出动态规划递推式:

\(f[x][y]=\frac{1}{2}f[x][y-1]+\frac{1}{3}f[x-1][y-1]\)

最后小明就会跳到 \(f[u][1]\)\(u\) 为正整数且 \(u\leqslant n\))(这里 \(u\) 是代表当小明为第一个人时,总共还剩几人),小明就到了第一个,上帝要为他摇骰子了!而当小明为第一个时,\(j=1\),所以没有 \(f[u][0]\)\(f[u-1][0]\),所以我们要单独推当 \(j=1\) 时的情况:他有 \(\frac{1}{6}\) 的概率会胜利,而又会有 \(\frac{1}{3}\) 的概率出局(要求小明的胜率,这种情况不讨论),也有 \(\frac{1}{2}\) 跳到队尾,也就是跳到 \(f[u][u]\)(又是一个天道好轮回),所以 \(f[u][1]=\frac{1}{6}+\frac{1}{2} f[u][u]\)

这就可以推广到所有 \(j=1\) 时的情况,因此我们就会建立这么多个循环往复的等式,这是一个连续的多元一次方程组,是容易解决的,观察一下系数,然后就可以用代入消元法解决了。(谢谢 Aw顿顿 )

观察系数的代码如下:

for(int j=2; j<=i; j++){//观察系数
	sum=sum/2;
	num=num/2+f[i-1][j-1]/3;
}

总结:

  1. 坑点(也不算吧):输出保留九位小数。
  2. 需要的基础:dp+数学(数论)。
  3. dp 比较别致一点,因为 dp 数列可能会构成一个环,所以须要观察系数,然后就可以用代入消元法解决了。
  4. 当小明第一个人时,若遇到第二种情况,还需要重新排到队尾,千万不可结束,不然听取 WA 声一片。

废话不多说了,上总代码!

注释在代码里了

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m;
double f[1005][1005];
int main(){
	cin>>n>>m;
	f[1][1]=1;//初始化!只有一个人的时候你直接胜利!别忘了写这一行
	for(int i=2; i<=n; i++){
		double sum=0.5,num=1.0/6.0;
		//1.0/6.0和1/6可有大区别了!
		//1/6=0.166666667……,取整一下为0
		//1.0/6.0则是相当于(double)1/6
		for(int j=2; j<=i; j++){//观察系数
			sum=sum/2;
			num=num/2+f[i-1][j-1]/3;
		}
		//不断将f[i][j-1]带入f[i][j],最终就可以求出下面二式
		f[i][i]=num/(1-sum);//解出了f[i][i]
		f[i][1]=f[i][i]/2+1.0/6.0;//解出了f[i][1]
		for(int j=2; j<i; j++) f[i][j]=f[i][j-1]/2+f[i-1][j-1]/3;//前面推出的解析式(递推式?到底说算是什么啊……)
	}
	printf("%.9lf",f[n][m]);//保留九位输出
	return 0;
} 
//P上水印
小明结束了他的演艺生涯。。。
posted @ 2025-01-29 15:24  naroto2022  阅读(31)  评论(0)    收藏  举报