zoukankan      html  css  js  c++  java
  • 【ybtoj】【期望dp】彩球抽取

    题意

    题目描述
    袋子里有 (n) 个球。每次依次取出两个,把第二个球涂成第一个球的颜色,然后放回袋里搅匀。你的任务是算出让所有球的颜色相同所需要的取球次数期望。
    输入格式
    输入仅一行,包含一个长度小于 (25) 的字符串,每个字符为一个大写英文字母,代表一个球的颜色。
    输出格式
    输出仅一行,表示答案,保留 (6) 位小数。
    样例
    样例输入1
    AB
    样例输出1
    1.000000
    样例输入2
    ZCZ
    样例输出2
    3.000000
    样例输入3
    AAABB
    样例输出3
    11.666667
    数据范围
    (n le 25)

    题解

    正常考虑每个球每次取的状态很难,所以要转化思路。
    (dp(i,j,k)) 表示前 (i) 轮,颜色为 (j) 的球有 (k) 个的期望步数。
    那么答案就是 (sumlimits_{i=0}^{25} dp(i,j,n) imes i).
    这样就成功把不好表示的球颜色的状态表示了,因为状态变得只和当前所求颜色有关。
    但是这样循环 (i) 的上界在哪里?要求保留 6 位小数,结合时间上限大概可以设上界为 (50000~100000)。(题解里说 (6) 位小数精度不是特别高)
    关于 (i) 这一维,可以用滚宫数组优化。
    PS:本人写的时候 (n) 在全局和主函数里分别定义了一遍,调吐血了...$

    代码

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    const int INF = 0x3f3f3f3f;
    inline ll read()
    {
    	ll ret=0;char ch=' ',c=getchar();
    	while(!(c>='0'&&c<='9')) ch=c,c=getchar();
    	while(c>='0'&&c<='9') ret=(ret<<1)+(ret<<3)+c-'0',c=getchar();
    	return ch=='-'?-ret:ret;
    }
    int n;
    double dp[30][30][2];
    int cnt[30],roll;
    char s[30];
    inline void clear()
    {
    	for(int i=0;i<26;i++) 
    		for(int j=0;j<=n;j++) 
    			dp[i][j][roll]=0;
    }
    int main()
    {
    	scanf("%s",s+1);
    	n=strlen(s+1);
    	for(int i=1;i<=n;i++) cnt[s[i]-'A']++;
    	for(int i=0;i<26;i++) if(cnt[i]) dp[i][cnt[i]][0]=1;
    	double res=0;
    	for(int stp=0;stp<=50000;stp++)
    	{
    		//printf("###Stp=%d:
    ",stp);
    		for(int i=0;i<26;i++) 
    		{
    			//if(dp[i][n][roll]) printf("dp[%d][%d]=%.3lf
    ",i,n,dp[i][n][roll]);
    			res+=stp*dp[i][n][roll];
    		}
    		//printf("res=%.3lf
    ",res);
    		//printf("roll:%d
    ",roll);
    		roll^=1;
    		clear();
    		for(int i=0;i<26;i++) 
    			for(int j=1;j<n;j++) 
    			{
    				double prob=1.0*j*(n-j)/n/(n-1);//同时选出一个i颜色和和一个非i颜色的球的可能性 
    				//printf("prob=%.3lf
    ",prob);
    				dp[i][j-1][roll]+=dp[i][j][roll^1]*prob/2;
    				dp[i][j+1][roll]+=dp[i][j][roll^1]*prob/2;
    				dp[i][j][roll]+=dp[i][j][roll^1]*(1-prob);
    				//printf("dp1=%.3lf,dp2=%.3lf,dp3=%.3lf
    ",dp[i][j][roll],dp[i][j-1][roll],dp[i][j][roll]);
    				//printf("dp[%d][%d][%d]=%.3lf
    ",i,j,roll^1,dp[i][j][roll^1]);
    			}
    	}
    	printf("%.6lf",res);
    	return 0;
    }
    
    
  • 相关阅读:
    spoj 7001 Visible Lattice Points莫比乌斯反演
    codeforces 446C DZY Loves Fibonacci Numbers 数论+线段树成段更新
    fzu 1753 质因数的应用
    hud 4746 莫比乌斯反演
    hdu 1695 容斥原理或莫比乌斯反演
    hdu 4741 Save Labman No.004异面直线间的距离既构成最小距离的两个端点
    codeforces练习
    年底Android面试整理(附答案)
    最近Android真的凉凉了?
    Android 应用防止被二次打包指南
  • 原文地址:https://www.cnblogs.com/conprour/p/15318516.html
Copyright © 2011-2022 走看看