合成大西瓜
题意:
现在有 4 种球,分别是一级球,二级球,三级球和四级球,它们的体积都是一样的。有一个垂直的管道,上端开口,下端封底,初始管道为空,管道的宽度只能容纳一个球,管道长度是无限的。当两个一级球相邻时,就可以变成一个二级球,当两个二级球相邻时,就可以变成一个三级球,当两个三级球相邻时,就可以变成一个四级球。每次操作都会从管道上方随机放入一个一、二或三级球,概率都是三分之一。当游戏出现一个四级球的时候,就代表胜利。
现在 cccd 已经将游戏玩到一个局面了,他想知道在此局面上达到胜利所需操作的期望次数
题解:
球只能升级,大的球的前面的小球不能被用到。
所以一共就9种状态{{4}, {3, 2, 1}, {3, 2}, {3, 1}, {2, 1}, {2}, {1}, {3},{}}
给这些状态编号,我们能写出他们转移的概率。B[i][j]表示状态从i转移到j的概率。
double b = (double)1 / 3; vector<vector<double>> B(9, vector<double>(9)); B[0][0] = 1; B[1][0] = B[1][5] = B[1][7] = b; B[2][0] = B[2][1] = B[2][7] = b; B[3][2] = B[3][5] = B[3][7] = b; B[4][5] = b; B[4][7] = b + b; B[5][4] = b; B[5][7] = b + b; B[6][7] = b; B[6][5] = b + b; B[7][2] = B[7][3] = B[7][0] = b; B[8][5] = B[8][6] = B[8][7] = b;
我们用另一个矩阵表示当前状态在各位置的可能(当前状态在它的序号位置值为1,表示100%在这里)。
vector<double> A(9);
矩阵A*B就是转移一次后状态转移后在各位置的可能,9个位置,每个位置的数值表示状态在当前位置的概率。
用结果迭代A,每迭代n次,表示n次操作后状态在各位置的概率。
这就是马尔科夫链,A会趋近一个固定值,这里,我们可以想象,n很大的时候,在0的位置,也就是赢的概率趋近1。
表示cnt次能到的概率,
就表示cnt次转移到达终点的概率(
会很快趋近于0)。
那么到达终点的期望次数为:(
会很快趋近于0,所以很快能算出来)
代码:
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
void matrixMultiply(vector<double> &A, vector<vector<double>> &B)
{
int n = B.size();
vector<double> result(n, 0);
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
result[i] += A[j] * B[j][i];
}
}
result.swap(A);
return;
}
vector<double> A(9);
vector<vector<double>> B(9, vector<double>(9));
vector<vector<int>> C = {{4}, {3, 2, 1}, {3, 2}, {3, 1}, {2, 1}, {2}, {1}, {3},{}};
double b = (double)1 / 3;
int main(int argc, char *argv[])
{
// ios::sync_with_stdio(false);
// cin.tie(0);
B[0][0] = 1;
B[1][0] = B[1][5] = B[1][7] = b;
B[2][0] = B[2][1] = B[2][7] = b;
B[3][2] = B[3][5] = B[3][7] = b;
B[4][5] = b;
B[4][7] = b + b;
B[5][4] = b;
B[5][7] = b + b;
B[6][7] = b;
B[6][5] = b + b;
B[7][2] = B[7][3] = B[7][0] = b;
B[8][5] = B[8][6] = B[8][7] = b;
ll n ;
cin>>n;
vector<int> a(n);
for (int i = 0; i < n; i++)
{
cin>>a[i];
}
int l = 0, pos = 0;//l是上个位置的值,pos保存位置,这里找到最后的最大数的位置
for (int i = n - 1; i >= 0; i--)
{
if (a[i] > l)
{
l = a[i];
pos = i;
}
else
{
break;
}
}
vector<int> now;//当前的状态
for (int i = pos; i <= n - 1; i++)
{
now.push_back(a[i]);
}
for (int i = 0; i < 9; i++)
{
if (now == C[i])
{
A[i] = 1;//当前的状态对应的序号
}
}
double ans=0;//期望
int cnt=0;//操作次数
double pass=0;//上一个值
while (fabs(A[0]-1)>1e-8){
matrixMultiply(A, B);//迭代
cnt++;
ans+=(A[0]-pass)*cnt;
pass=A[0];
}
printf("%.8lf", ans);
return 0;
}