bzoj 2554 Color

Description

有n个球排成一列,每个球都有一个颜色,用A-Z的大写字母来表示,我们每次随机选出两个球ball1,ball2,使得后者染上前者的颜色,求期望操作多少次,才能使得所有球的颜色都一样?

Input

    一行一个字符串,表示球的颜色
   

Output

 一行表示结果,精确到小数点后1位。

       前排推荐阅读liurunda的题解,写的非常好,我感觉我只需要将如何推代码实现即可了,https://www.cnblogs.com/liu-runda/p/6803126.html。

       话说那样说还是大概要说一下做法的,我们可以枚举最终会成为哪个颜色,算一下成为该颜色的概率,再算一下变成该颜色的期望。

       首先我们先算一下成为该颜色的概率是多少,由于最终颜色固定其他颜色都是干扰颜色了,所以我们可以用dp1[i]表示有i个目标颜色要全变成目标颜色时的概率,那么只有两种可能转移到少一种或多一种,即dp1[i]=0.5dp[i-1]+0.5dp[i+1],我们又有边界条件dp1[0]=0,dp1[n]=1可以通过o(n)的方法来解出来每一个dp1的值,怎么推后文会说,不过这里发现dp1[i]=i/n的结论。

       接着算变成同一种颜色的期望,设dp2[i]表示有i个目标颜色,要全变成目标颜色时的期望步数,由于从不同的i转移到同一种颜色的比重是不一样的,所以我们还要乘上它的比重,我们就可以得到dp2[i]=改变1的期望步数+(i-1更新到答案的比重)*dp2[i-1]+(i+1更新到答案的比重*dp2[i+1]),那么i-1有i-1/n的可能转移到答案,i+1同理,那么i-1的比重就是i-1/(i-1+(i+1)即i-1/2*i。最后需要解决的就是改变1的期望步数,我们总共有n*(n-1)种可能的选法,有2*i*(n-i)的方法是可以变化的,所以那么改变1的期望步数就是1/(2*i*(n-i)/(n*(n-1))顺便设每个i的期望步数为g[i]好了。

       最后我们有了这个方程可以通过类似迭代的方法求得解,因为首先dp2[1]转移到dp2[0]的比重是0,所以我们就能得到dp2[1]=(1+1)/2*dp[2]+g[1],是不是很像kx+b的形式,那么我们就令dp2[i]=k[i]*dp[i+1]+b[i],k[1],b[1]是可知的,那么dp2[i]的方程有dp2[i]=g[i]+ (i-1)/2*i*dp2[i-1]+ (i+1)/(2*i)*dp2[i+1],那么我们把dp2[i-1]的带进去,中间那一项就变成了((i-1)/(2*i)*(k(i-1)*dpi+b(i-1)),常数项与常数项合并,dpi那一项与dpi合并,再将新的dpi的系数除过去,就可以得到新的dp2[i]=k[i]*dp2[i+1]+b[i]了,最后由于dp2[n]=0,所以dp2[n-1]=b[n-1],所以我们一路更新回去,就得到了dp2[1~n]的值了,说的好乱,按我的在纸上画画就可以推出来了。

      p.s.模拟赛暴力随机得了5分

      最后我们用每一个数出现的次数乘上它变成答案的概率求和即可。

      下附AC代码。

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define maxn 10005
using namespace std;
int n;
char s[maxn];
int cnt[maxn];
double dp[maxn],k[maxn],b[maxn],g[maxn];
int main()
{
	scanf("%s",s+1); n=strlen(s+1);
	for(int i=1;i<n;i++)
	{
		g[i]=(1/((double)(2.0*i*(n-i)/(double)(n*(n-1)))));
	}
	
	b[1]=g[1];k[1]=1.0;
	for(int i=2;i<n;i++)
	{
		b[i]=g[i]+(double)(i-1)/(double)(2.0*i)*b[i-1];
		double temp=(1.0-(((double)(i-1)/(double)(2*i))*k[i-1]));
		b[i]=b[i]/temp;
		k[i]=(double)(i+1)/(2.0*i)/temp;
	}
	
	dp[n-1]=g[n-1];
	for(int i=n;i>=1;i--)
	dp[i]=k[i]*dp[i+1]+b[i];
	for(int i=1;i<=n;i++)
	cnt[s[i]]++;
	
	double ans=0.0;
	for(int i='A';i<='Z';i++)
	ans+=(dp[cnt[i]]*cnt[i]/n);
	printf("%.1lf\n",ans);
}


        


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值