题意
36张牌分成9堆,每堆4张牌。每次可以拿走某两堆顶部的牌,但需要点数相同。如果有 多种拿法则等概率的随机拿。例如,9堆顶部的牌分别为KS, KH, KD, 9H, 8S, 8D, 7C, 7D, 6H,则有5种拿法(KS,KH), (KS,KD), (KH,KD), (8S,8D), (7C,7D),每种拿法的概率均为1/5。 如果最后拿完所有牌则游戏成功。按顺序给出每堆牌的4张牌,求成功概率。
分析
为啥一张牌是两个字符组成??这什么奇怪的牌??按照题面给的例子就是说,只要第一个字符相同就OK了吧。。
于是我们考虑预处理存每一堆牌的每一位的花色的状态,然后因为牌的数量很少,开个九维的dp来存状态,用记忆化搜索转移。
其实转移方法就很简单了,在dfs里枚举相同的两堆堆顶,然后把这两堆顶部的牌取走作为下一个状态,计算一共有多少方案数以及这些方案数成功的概率之和。
那么本状态成功的概率就是 ∑可达的下一个状态成功概率 ÷ 可达的方案数
而边界就是全部取光的状态的成功概率为100%
代码
- #include<bits/stdc++.h>
- using namespace std;
- #define N 6
- int a[15][15];
- char s[15][15];
- int vis[N][N][N][N][N][N][N][N][N];
- double dp[N][N][N][N][N][N][N][N][N];
- inline void init()
- {
- memset(dp,0,sizeof(dp));
- memset(vis,0,sizeof(vis));
- }
- inline double dfs(int p1,int p2,int p3,int p4,int p5,int p6,int p7,int p8,int p9)
- {
- if(vis[p1][p2][p3][p4][p5][p6][p7][p8][p9])return dp[p1][p2][p3][p4][p5][p6][p7][p8][p9];
- vis[p1][p2][p3][p4][p5][p6][p7][p8][p9]=1;
- int ok=1,tot=0,tmp[10]={0,p1,p2,p3,p4,p5,p6,p7,p8,p9};
- for(int i=1;i<=9;i++)
- if(tmp[i]){ok=0;break;}
- if(ok)return dp[p1][p2][p3][p4][p5][p6][p7][p8][p9]=1.0;
- double possibility=0.0;
- for(int i=1;i<=9;i++)
- for(int j=i+1;j<=9;j++)
- if(tmp[j]&&tmp[i]&&a[i][tmp[i]]==a[j][tmp[j]])
- {
- tmp[i]--,tmp[j]--;tot++;
- possibility+=dfs(tmp[1],tmp[2],tmp[3],tmp[4],tmp[5],tmp[6],tmp[7],tmp[8],tmp[9]);
- tmp[i]++,tmp[j]++;
- }
- if(tot)dp[p1][p2][p3][p4][p5][p6][p7][p8][p9]=possibility/(1.0*tot);
- return dp[p1][p2][p3][p4][p5][p6][p7][p8][p9];
- }
- int main()
- {
- while(~scanf("%s%s%s%s",s[1],s[2],s[3],s[4]))
- {
- init();
- for(int j=1;j<=4;j++)a[1][j]=s[j][0]-'0';
- for(int i=2;i<=9;i++)
- for(int j=1;j<=4;j++)
- {
- scanf("%s",s[j]);
- a[i][j]=s[j][0]-'0';
- }
- dfs(4,4,4,4,4,4,4,4,4);
- printf("%.6lf\n",dp[4][4][4][4][4][4][4][4][4]);
- }
- }