题意:给一个有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]+=(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]+=(val[i]+dp[k])*p; (val[i]表示该事件的消费或价值,p表示发生当前事件的概率)