Gym - 101174D[概率期望DP]

版权声明:欢迎随便转载。 https://blog.csdn.net/a1214034447/article/details/89841891

题目链接:https://vjudge.net/problem/Gym-101174D

解题思路:

其实数字是什么根本不重要,因为它们概率是相同的,关键的是在于A和B中卡片个数相同的数量,所以其实我们可以把所有转态压缩成一个10*10*10的三维dp(其实还远远不到这么多)。

所以我们可以将2*C个数分为,m,w,m,2*m + 2*w = 2*C,m表示A的卡片中B没有的个数(所以B中A没有的其实也是m),还有就是w张A和B中都有的。

dp[i][j][k]表示已经拿了i张A中只有的,j张A和B都有的,k张B中只有的情况下,还能再玩的轮数期望。

很明显当i+j==C||k+j==C时,dp[i][j][k] == 0。令tot=(A中的卡片∪B中的卡片),那么我们就有了一个转移方程:

令概率dp[i][j][k]转移到dp[i+a][j+b][k+c]的概率为p[a][b][c],那么:

p[a][b][c] = \frac{\binom{m-i}{a}*\binom{w-j}{b}*\binom{m-k}{c}*\binom{i+j+k+n-tot}{D-(a+b+c)}}{\binom{n}{D}}       

那么就有:

dp[i][j][k] = 1 + \sum_{a=0}^{m-i}\sum_{b=0}^{w-j}\sum_{c=0}^{w-k}p[a][b][c]*dp[i+a][j+b][k+c]

把右边出现的一次dp[i][j][k]移项到左边,就是一个完美的公式了。

因为w+m+m<=2*C<=20,所以时间复杂度不会超过O(7^{6})。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mx = 1<<21;
int n,a[100],b[100],D,C,w,m; 
int tot;
double dp[12][12][12];
ll c[100][100],base;
bool vis[mx];
void init(){
	c[0][0] = 1;
	for(int i=1;i<55;i++){
		c[i][0] = c[i][i] = 1;
		for(int j=1;j<min(11,i);j++){
			c[i][j] = c[i-1][j] + c[i-1][j-1]; 
		}
	}
}
double upt(int x,int y,int z)
{
	if(x+y==C||z+y==C) return 0;
	double ret = 0,ra = 0;
	int cnt = x+y+z+n-tot;
	for(int i=0;i<=m-x;i++){
		for(int j=0;j<=w-y;j++){
			for(int k=0;k<=m-z;k++){
				if(i+j+k>D) continue;
				if(i==0&&j==0&&k==0){
					ra = 1.0*c[cnt][D]/base;
					continue;
				}
				int s1 = D - (i+j+k);
				ll tmp = c[m-x][i]*c[w-y][j]*c[m-z][k];
				ret += 1.0*tmp*c[cnt][s1]/base*dp[i+x][j+y][k+z];
			}
		}
	}
	return (1+ret)/(1-ra);
}
int main()
{
	init();
	scanf("%d%d%d",&n,&D,&C);
	base = c[n][D];
	for(int i=1;i<=C;i++) scanf("%d",a+i),vis[a[i]] = 1;
	for(int i=1;i<=C;i++){
		scanf("%d",b+i);
		if(vis[b[i]]) w++; 
	} 
	m = C - w,tot = 2*C - w;
	for(int i=m;i>=0;i--){
		for(int j=w;j>=0;j--){
			for(int k=m;k>=0;k--){
				dp[i][j][k] = upt(i,j,k);
			}
		}
	}
	printf("%.7lf\n",dp[0][0][0]);
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/a1214034447/article/details/89841891