UVA - 1637 Double Patience (概率dp+状态保存)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/a54665sdgf/article/details/80722335

题目大意:36张牌分成9堆,每堆4张牌,你每次可以任取两堆顶部的牌,但这两张牌点数必须相同。求把所有的牌都拿走的概率。

思路:可以用dfs的方法模拟抽牌过程。我们定义状态为“每堆牌剩余数量的集合”,由于重复状态较多,而且总状态数较少(5^9),所以可以用dp的思想,记忆化搜索。

这道题的关键是每种状态下的dp值以及每堆牌的数量该如何保存。

最容易想到的是定义一个9维数组,每堆牌的张数用一个维度表示,但这种方法写起来太麻烦而且不美观。

另一种方法是用5进制编码来表示9堆牌的剩余数量,代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define FRER() freopen("i.txt","r",stdin)
using namespace std;
const int N=2000000;
double d[N];
char a[10][5];
int Pow(int x,int e)
{
    int res=1;
    while(e)
    {
        if(e&1)
            res*=x;
        x*=x;
        e>>=1;
    }
    return res;
}
double dp(int x)
{
    if(d[x]>=0)
        return d[x];
    int p,q,cnt=0;
    double sum=0;
    for(int i=0; i<9; ++i)
        for(int j=i+1; j<9; ++j)
        {
            p=x%(Pow(5,i+1))/Pow(5,i);
            q=x%(Pow(5,j+1))/Pow(5,j);
            if(p&&q&&a[i][p]==a[j][q])
            {
                sum+=dp(x-Pow(5,i)-Pow(5,j));
                cnt++;
            }
        }
    return d[x]=cnt?sum/cnt:0;
}
int main()
{
    //FRER();
    while(1)
    {
        fill(d,d+N,-1);
        d[0]=1;
        for(int i=0; i<9; ++i)
            for(int j=1; j<=4; ++j)
                if(scanf(" %c%*c",&a[i][j])!=1)
                    return 0;
        int x=0;
        for(int i=0; i<9; ++i)
            x+=4*Pow(5,i);
        printf("%f\n",dp(x));
    }
    return 0;
}

但这种方法的缺点是需要不断地编码和解码,浪费了许多不必要的时间。一种改进的方法是用一个9元组c[9]来表示每堆牌剩余的数量,由于每次dp的时候只需要知道当前的状态,所以只需在每次dp前后修改一下这个9元组的值即可。为了简洁表示,仍然用编码的方式来保存dp值,但不需要频繁地编码和解码了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define FRER() freopen("i.txt","r",stdin)
using namespace std;
const int N=2000000;
double d[N];
char a[10][5];
int c[9];
double dp()
{
    int x=0;
    for(int i=0; i<9; ++i)
        x=x*5+c[i];
    if(d[x]>=0)
        return d[x];
    int cnt=0;
    double sum=0;
    for(int i=0; i<9; ++i)
        for(int j=i+1; j<9; ++j)
        {
            if(c[i]&&c[j]&&a[i][c[i]]==a[j][c[j]])
            {
                c[i]--,c[j]--;
                sum+=dp();
                c[i]++,c[j]++;
                cnt++;
            }
        }
    return d[x]=cnt?sum/cnt:0;
}
int main()
{
    //FRER();
    while(1)
    {
        fill(d,d+N,-1);
        fill(c,c+9,4);
        d[0]=1;
        for(int i=0; i<9; ++i)
            for(int j=1; j<=4; ++j)
                if(scanf(" %c%*c",&a[i][j])!=1)
                    return 0;
        printf("%f\n",dp());
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/a54665sdgf/article/details/80722335