Topcoder SRM 651 div1 250 题解 (概率dp)

题意:给一个有n个整数的数组d。第一次随机选择一个数,以后每次随机选择一个没选过的数,如果这个数的下标大于前一个数,则不要这个数并停止选择。否则,继续选数,除非所有的数都选择就停止选择。问最后选出来的数的和的期望值。n<=250.

题解:这题有多种做法。

方法一:用dp[i][j] 表示下标为i到n的数选了j个,第i个数必选的期望值。我们转移的时候,需要记录每个状态的概率值。所以我们用f[i][j] 表示当前状态下的概率值。最后计算答案的时候在计算终止的概率。详情见代码:

#line 4 "RandomPancakeStack.cpp"
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <string>
#include <sstream>
#define OUT(x) cout << #x << ": " << (x) << endl
#define SZ(x) ((int)x.size())
#define FOR(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
typedef long long LL;
double dp[310][310],p[310][310];
class RandomPancakeStack
{
public:
    double expectedDeliciousness(vector <int> d)
    {
        int n=d.size();
        int i,j,k;
        for(i=0;i<n;i++)
        {
            dp[1][i]=d[i]*1.0/n;
            p[1][i]=1.0/n;
        }
        for(i=2;i<=n;i++)
        {
            for(j=0;j<=n-i;j++)
            {
                dp[i][j]=0;
                p[i][j]=0;
                for(k=j+1;k<=n-i+1;k++)
                {
                    dp[i][j]+=dp[i-1][k]/(n-i+1)+d[j]*p[i-1][k]/(n-i+1);
                    p[i][j]+=p[i-1][k]/(n-i+1);
                }
            }
        }
        double re=0;
        for(i=1;i<=n;i++)
        {
            for(j=0;j<=n-i;j++)
            {
                if(i==n)
                {
                    re+=dp[i][j];
                }
                else
                {
                    re+=dp[i][j]*(n-j-i)/(n-i);
                }
            }
        }
        return re;
    }


};


// Powered by FileEdit
// Powered by TZTester 1.01 [25-Feb-2003]
// Powered by CodeProcessor

方法二:分别计算每个数的期望值,而计算这个数的期望值需要计算所有选择到这个数的概率。所以我们用dp[i][j]表示i+1到n已经选了j个数,然后选到第i个数的概率。最后再统计答案:

#line 4 "RandomPancakeStack.cpp"
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <string>
#include <sstream>
#define OUT(x) cout << #x << ": " << (x) << endl
#define SZ(x) ((int)x.size())
#define FOR(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
typedef long long LL;
double dp[310][310];
class RandomPancakeStack
{
public:
    double expectedDeliciousness(vector <int> d)
    {
        int i,j,k;
        int n=d.size();
        for(i=0;i<n;i++)
        {
            dp[0][i]=1.0/n;
        }
        for(i=1;i<n;i++)
        {
            for(j=0;j<n-i;j++)
            {
                for(k=j+1;k<=n-i;k++)
                {
                    dp[i][j]+=dp[i-1][k]/(n-i);
                }
            }
        }
        double re=0;
        for(i=0;i<=n-1;i++)
        {
            for(j=0;j<n-i;j++)
            {
                re+=d[j]*dp[i][j];
            }
        }
        return re;
    }


};


// Powered by FileEdit
// Powered by TZTester 1.01 [25-Feb-2003]
// Powered by CodeProcessor

方法三:用dp[i][j]表示当前可以从i-1到0中选,共有j个数没被选的期望值。那么最后答案就是dp[n][n]。

那么转移就是:

dp[i][j]+=(d[k]+dp[k][j-1])/j 。0<=k<i

代码如下:

#line 4 "RandomPancakeStack.cpp"
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <string>
#include <sstream>
#define OUT(x) cout << #x << ": " << (x) << endl
#define SZ(x) ((int)x.size())
#define FOR(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
typedef long long LL;
double dp[310][310];
class RandomPancakeStack
{
public:
    double expectedDeliciousness(vector <int> d)
    {
        int i,j,k;
        int n=d.size();
        memset(dp,0,sizeof(dp));
        for(i=1;i<=n;i++)
        {
            for(j=1;j<=n;j++)
            {
                for(k=0;k<i;k++)
                {
                    dp[i][j]+=(d[k]+dp[k][j-1])/j;
                }
            }
        }
        return dp[n][n];
    }


};


// Powered by FileEdit
// Powered by TZTester 1.01 [25-Feb-2003]
// Powered by CodeProcessor


// Powered by FileEdit
// Powered by TZTester 1.01 [25-Feb-2003]
// Powered by CodeProcessor
 
总结:对于求解期望类问题一般是用dp[i]表示从状态当前i走到结束状态的期望值,按照事件发生的先后顺序就行转移,转移一般是:

dp[i]+=(val[i]+dp[k])*p;  (val[i]表示该事件的消费或价值,p表示发生当前事件的概率)

你可能感兴趣的:(动态规划,ACM,概率论)