bzoj 2554 Color 期望DP

期望DP

枚举最终能成为哪个颜色,把这个颜色看做白球,其余颜色看成黑球。最后分别把每种颜色的期望加起来就行。

考虑当前有i个白球,全变成白球期望步数设为f[i]

一次操作可能造成有白球变黑或者黑球变白的概率:\(\frac{i(n-i)}{C^2_n}\),也就是期望\(\frac{C^2_n}{i(n-i)}\)次操作会造成有白球变黑或者黑球变白。

很容易写错成

\(f[i]=\frac{C^2_n}{i(n-i)}+\frac{1}{2}∗f[i−1]+\frac{1}{2}∗f[i+1]\)

为什么是错的?你考虑一下f[1],能从f[0]转移来吗?不行的,f[0]没意义。

所以我们重新考虑f,f[i]应该是当前有i个白球,在能全变成白球条件下,全变成白球期望步数

设当前有i个白球,最终能全变成白球的概率是什么?设为g[i],每次操作等概率减少或增加白球,可知\(g[i]=\frac{1}{2}g[i-1]+\frac{1}{2}g[i+1]\)

且有\(g[0]=0\)\(g[n]=1\)可知是等差数列,\(g[i]=\frac{i}{n}\)

那么f[i]按照g[i-1]和g[i+1]的比例来确定f[i-1]和f[i+1]前的系数,并且要注意这俩系数和为1

可得\(f[i]=\frac{C^2_n}{i(n-i)}+\frac{(i−1)}{2i}∗f[i−1]+\frac{i+1}{2i}∗f[i+1]\)

这个方程很特殊,可以O(n)消元解方程

#include<iostream>
#include<cstring>
#include<cstdio>
#define DB double
using namespace std;
int n;
DB ans;
const int N=10010;
int tong[30];
DB f[N],c[N][5];
char s[N];
int main()
{
	scanf("%s",s+1);n=strlen(s+1);
	for(int i=1;i<=n;++i)++tong[s[i]-'A'+1];
	for(int i=1;i<=n;++i)//c[i][2]是f[i]的系数 c[i][1]是f[i-1]的系数 c[i][3]是f[i+1]的系数 c[i][4]是方程等号右边的常数
 	{
		if(i!=1&&i!=n)c[i][1]=-1.0*(i-1)/(2*i);
		c[i][2]=1;
		if(i!=n)c[i][3]=-1.0*(i+1)/(2*i);
		if(i!=n)c[i][4]=((1.0*n*n-n)/2.0)/(1.0*i*(n-i));
	}
	for(int i=2;i<=n;++i)//消去f[i-1]
	{
		double bei=c[i][1]/c[i-1][2];
		c[i][1]=0;c[i][2]-=c[i-1][3]*bei;
		c[i][4]-=c[i-1][4]*bei;
	}
	for(int i=n-1;i>=1;--i)//消去f[i+1]
	{
		double bei=c[i][3]/c[i+1][2];
		c[i][3]=0;
		c[i][4]-=c[i+1][4]*bei;
	}
	
	for(int i=1;i<=n;++i)c[i][4]/=c[i][2];//此操作后c[i][4]等于f[i]
	for(int i=1;i<=26;++i)ans+=(DB)tong[i]/n*c[tong[i]][4];//统计答案
	printf("%0.1f",ans);
	return 0;
}
posted @ 2023-01-14 21:45  wljss  阅读(108)  评论(0编辑  收藏  举报