如果我们可以求出两个人n张牌取k张的所有情况,问题就会变成一个简单的排列问题:A获胜的概率=A k张牌比B k张牌大的情况/总情况.然后我们可以用dp处理出n张牌中取k张牌的所有情况,dp[i][j][k]表示前i张牌中取j张牌构成k点的情况数,
状态转移:dp[i][j][k]=dp[i-1][j][k]+k>=a[i]?dp[i-1][j-1][k-a[i]]:0;
然后就是在计算概率时因为数比较大,必须用double,并且采取除两次的方法,避免总情况数过大导致溢出.
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
double dp[55][55][650];
int a[55],b[55];
int check(char ch)
{
if(ch==‘A‘) return 1;
if(ch==‘X‘) return 10;
if(ch==‘J‘) return 11;
if(ch==‘Q‘) return 12;
if(ch==‘K‘) return 13;
return ch-‘0‘;
}
int main()
{
int n,k,t;
scanf("%d",&t);
while(t--)
{
char str[3];
double C=1;
double f1[650],f2[650];
int sum1=0,sum2=0;
scanf("%d%d",&n,&k);
for(int i=1;i<=k;i++)
C=C*(n-i+1)/i;
for(int i=1;i<=n;i++)
{
scanf("%s",str);
a[i]=check(str[0]);
sum1+=a[i];
}
for(int i=1;i<=n;i++)
{
scanf("%s",str);
b[i]=check(str[0]);
sum2+=b[i];
}
memset(dp,0,sizeof(dp));
for(int i=0;i<=n;i++)
dp[i][0][0]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=i&&j<=k;j++)
for(int d=1;d<=sum1;d++)
{
if(d>=a[i])
dp[i][j][d]=dp[i-1][j][d]+dp[i-1][j-1][d-a[i]];
else
dp[i][j][d]=dp[i-1][j][d];
}
for(int i=1;i<=sum1;i++)
f1[i]=1.0*dp[n][k][i]/C;
memset(dp,0,sizeof(dp));
for(int i=0;i<=n;i++)
dp[i][0][0]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=i&&j<=k;j++)
for(int d=1;d<=sum2;d++)
{
if(d>=b[i])
dp[i][j][d]=dp[i-1][j][d]+dp[i-1][j-1][d-b[i]];
else
dp[i][j][d]=dp[i-1][j][d];
}
f2[0]=0.0;
for(int i=1;i<=sum1;i++)
{
f2[i]=1.0*dp[n][k][i]/C;
f2[i]+=f2[i-1];
}
double cnt=0;
for(int i=1;i<=sum1;i++)
cnt+=(double)f1[i]*f2[i-1];
printf("%.6lf\n",1.0*cnt);
}
return 0;
}
时间: 2024-12-19 01:57:05