题意
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 ",dp[4][4][4][4][4][4][4][4][4]); } }