UVA 1637 NEERC 2005 (全概率公式)
首先熟悉概率公式的定义:
P(A)=P(A|B1)*P(B1)+P(A|B2)*P(B2)+P(A|B3)*P(B3)+…+P(A|Bn)*P(Bn),其中B1∪B2∪…∪Bn构成事件空间的全集。
这道题的意思是,有36张牌,分成9叠,每叠4张,每次可以任从两叠的顶端取两张相同的牌,直到取完即为成功,如果当前情况有有种选择,每种选择的概率的相等的,求成功的概率。
我们可以用一个九元组<a1,a2,…,a9>表示当前的状态,其中ai表示第i叠还有几张牌,取值为0~4,所以我们可以用一个5进制整数取表示一个状态,在dfs时进行解码和编码即可。可以用数组dp[x]记录当前状态下的概率,这样就不用重复计算相同状态。递归边界时dp[0]=1.0。
不要想的太复杂,一开始我想着解码和编码进行的整数运算太多会不会导致很慢,以至于我换成用二进制取表示状态且用位运算进行解码和编码。这样一来不仅仅多出了许多无用的状态数,大大浪费了空间,而且每次用memset初始化一个大数组会带来一定耗时,速度反而变得很慢。
并不是一个难题
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int max_n=2e6;
int card[10][5];
double dp_[max_n];
bool vis[max_n];
double dp(int cur)
{
if(vis[cur])return dp_[cur];
double ans=0.0;
vis[cur]=1;
int cnt=0;
int a[9];
int t=cur;
for(int i=0;i<9;i++)
{
a[i]=t%5;
t/=5;
}
for(int i=0;i<9;i++)
for(int j=i+1;j<9;j++)
if(a[i]&&a[j]&&card[i][a[i]]==card[j][a[j]])
{
a[i]--;a[j]--;
cnt++;
int ne=0;
for(int k=8;k>=0;k--)
ne=ne*5+a[k];
ans+=dp(ne);
a[i]++;a[j]++;
}
if(cnt==0) return dp_[cur]=0.0;
return dp_[cur]=ans/cnt;
}
int main(void)
{
string s;
// freopen("out.txt","w",stdout);
int start=0;
for(int i=0;i<9;i++)
start=start*5+4;
while(cin>>s)
{
memset(vis,0,sizeof(vis));
dp_[0]=1.0;vis[0]=1;
card[0][1]=s[0]-'0';
for(int i=0;i<9;i++)
for(int j=1;j<=4;j++)
if(i==0&&j==1)continue;
else{
cin>>s;
card[i][j]=s[0]-'0';
}
printf("%.6f\n",dp(start));
}
// fclose(stdout);
}
//AS 9S 6C KS
//JC QH AC KH
//7S QD JD KD
//QS TS JS 9H
//6D TD AD 8S
//QC TH KC 8D
//8C 9D TC 7C
//9C 7H JH 7D
//8H 6S AH 6H