BZOJ 4008 (期望,动态规划)
曾经见过zzzzx做过这道题,他被精度问题卡了,没想到我做到了这道题,物是人非啊。
这道期望DP挺难做。
原因是状态的设计非常匪夷所思。
Orz stdcall
考虑每一回合的期望,但是由于题目的种种限制,只可以使用状压来解决问题。
我们考虑改变状态。
不妨计算出每张卡牌的概率。
那么我们设\(f[i]\)表示\(i\)这张卡牌的概率。
考虑如何推导f[i]
设\(dp[i][j]\)表示在所有的\(r\)轮中,前\(i\)张卡中一共出了\(j\)张的概率。
\(f[i]\)的转移
\[f[i] = \sum_{j > 0}dp[i - 1][j] * [1 - (1 -p[i])^{r - j}]\]
\(dp[i][j]\)的转移
\[dp[i][j] = \]
\[dp[i - 1][j] * [1 - (1 - p[i])^{r - j}] \]
\[ + \]
\[[j > 0]dp[i - 1][j - 1] * [1 - (1-p[i])^{r - j + 1}]\]
预处理\((1 - p[i])\)的值即可.
DP细节好多啊,发现拆成函数这样比较好调
/*header*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#include <map>
#include <queue>
#define gc getchar()
#define pc putchar
#define ll long long
#define mk make_pair
#define fi first
#define se second
using std::min;
using std::max;
using std::swap;
const int maxN = 220 + 7;
const double oe = 1.00000000000000;
inline int gi() {
int x = 0,f = 1;char c = gc;
while(c < '0' || c > '9') {if(c == '-')f = -1;c = gc;}
while(c >= '0' && c <= '9') {x = x * 10 + c - '0';c = gc;}return x * f;
}
double a[maxN][maxN] , b[maxN][maxN];
double dp[maxN][maxN] , f[maxN];
double p[maxN];
int d[maxN] , n, r;
void Pre() {
for(int i = 1;i <= n;++ i) {
b[i][0] = oe;
for(int j = 1;j <= r;++ j)
b[i][j] = b[i][j - 1] * (oe - p[i]);
}
return;
}
void Solve() {
dp[1][0] = b[1][r];
f[1] = dp[1][1] = 1- dp[1][0];
for(int i = 2;i <= n;++ i) {
for(int j = 0;j <= r;++ j) {
f[i] += dp[i - 1][j] * (oe - b[i][r - j]);
dp[i][j] += dp[i - 1][j] * b[i][r - j];
if( j ) dp[i][j] += dp[i - 1][j - 1] * (oe - b[i][r - j + 1]);
}
}
double ans = 0;
for(int i = 1;i <= n;++ i) ans += f[i] * d[i];
printf("%.10lf\n",ans);
}
int main() {
int T = gi();
while(T --) {
memset(f , 0, sizeof(f));
memset(dp , 0, sizeof(dp));
n = gi() , r = gi();
for(int i = 1;i <= n;++ i) scanf("%lf%d",&p[i] , &d[i]);
Pre();
Solve();
}
return 0;
}