题意:给一个有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表示发生当前事件的概率)