UVA-1637【Double Patience】(概率dp)

时间:2022-09-19 06:17:23

题目大意:现有九堆牌,每堆有四张牌,每次我们可以选择不在同一堆上的顶上的两张牌消掉(如果两张牌数字相同的话),且每次随机选择,问最终将全部牌消掉的概率。注意有多组数据输入!(不要像我一样,交了好几次才发现。。。)

解:其实是很裸的dp,但是因为每次有消掉第一层第二层第三层第四层或者已经全部消光,所以我们要开一个五进制的dp,具体怎么写程序中看吧,总的复杂度应该是

O(5^9*9*9)。

程序:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
using namespace std;
char s[10][10][10];
int n=9,m=4,wei[1009],bin[1009];
double dp[2000009];
pair<int,int>edge[1009];
//计算这个数所代表的五进制
void calc(int now)
{
  int tot=n-1;
  while (tot>=0)
  {
      wei[tot]=(now/bin[tot]);
      now=now%bin[tot];
      tot--;
  }
}
int main()
{
//注意有多组数据!
  while (scanf("%s",s[0][1])!=EOF)
  {
  scanf("%s%s%s",s[0][2],s[0][3],s[0][4]);
  for (int i=1;i<n;i++)
  for (int j=1;j<=m;j++)
  scanf("%s",s[i][j]);
  bin[0]=1;
  for (int i=1;i<=n;i++) bin[i]=bin[i-1]*(m+1);
  for (int i=0;i<bin[n];i++) dp[i]=0;
  dp[bin[n]-1]=1.0; 
  for (int i=bin[n]-1;i>0;i--)
  if (dp[i])
  {
      calc(i);
      int cnt=0,p=0;
      for (int j=0;j<n;j++) p+=m-wei[j];
    for (int j=0;j<n;j++)
    for (int k=j+1;k<n;k++)
//如果当前这两个位置的牌相同,且两个位置的牌都没有取完
    if ((s[j][wei[j]][0]==s[k][wei[k]][0])
    &&(wei[j]!=0)&&(wei[k]!=0))
    edge[++cnt]=make_pair(j,k);
//记录当前状态下,有多少种能消掉的操作。
    double now=(1.0/(double)cnt)*dp[i];
    for (int j=1;j<=cnt;j++) 
    {
      int a=edge[j].first,b=edge[j].second;
      int sum=i-bin[a]-bin[b];
      dp[sum]+=now;
    }
  }
  printf("%.6lf\n",dp[0]);
  }
  return 0;
}