题目
http://acm.hdu.edu.cn/showproblem.php?pid=5013
分析
状态压缩DP+分离参量+多重DP
设f[i][S]表示到达第i天的人的集合为S到所有人都停下的期望得分,那么
f[i][S]=(f[i+1][S1]+|S1|/|S|*Sum[S1][i+1])*P[S1]*Q[S-S1]+Sum[S][i]
其中|S|表示集合S中元素的个数,Sum[S][i]=sigma{h[j][i]),j属于S,P[S]=pi{p[j]},j属于S,Q[S]=pi{1-p[j]},j属于S
这个方程是O(N*2^(2M))的,显然会超时
设对任意S,Q[S]>0,对方程做参量分离,化简可以得到
f[i][S]=Q[S]*(P[S1]/Q[S1]*f[i+1][S1]+1/|S|*|S1|*P[S1]/Q[S1]*Sum[S1][i+1])+Sum[S][i]
其中P[S1]/Q[S1]*f[i+1][S1]和|S1|*P[S1]/Q[S1]*Sum[S1][i+1]只于S1有关
可以转化为b[S]=sigma(a[S1]),S1包含于S的模型,这个模型是一个经典的状压DP
设f[i][S]=sigma{a[S1]},S1包含于S且S1与S只有前i位出现不一样,那么转移:
f[i][S]=f[i-1][S],S[i]==0,
f[i-1][S]+f[i-1][S-2^i],S[i]==1
f[0][S]=a[S]
那么f[m][S]即为所求的b[S]
在上面DP的过程中套用这个DP就可以了,时间复杂度O(NM*2^M)
由于我们之前假设Q[S]>0,所以一开始要对p[j]=1的情况进行eps调整
代码
/************************************************** * Problem: HDU 5013 * Author: clavichord93 * State: Accepted **************************************************/ #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <cassert> using namespace std; const int MAX_N = 16; const int MAX_S = 1 << MAX_N; int n, m; double p[MAX_N]; int happiness[MAX_N][MAX_N]; int cntState; int cnt[MAX_S]; double P[MAX_S]; double Q[MAX_S]; int sum[MAX_S][MAX_N]; double f[MAX_S]; double g[MAX_S]; double h[MAX_S]; int main() { #ifdef LOCAL_JUDGE freopen("in.txt", "r", stdin); #endif while (scanf("%d %d", &m, &n) != EOF) { for (int i = 0; i < m; i++) { scanf("%lf", &p[i]); if (p[i] < 1 + 1e-9 && p[i] > 1 - 1e-9) { p[i] -= 1e-9; } //assert(1 - p[i] >= -1e-9); } for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { scanf("%d", &happiness[i][j]); } } //cout << "hehe" << endl; cntState = 1 << m; for (int mask = 0; mask < cntState; mask++) { P[mask] = 1; Q[mask] = 1; cnt[mask] = 0; for (int j = 0; j < m; j++) { if ((mask >> j) & 1) { P[mask] *= p[j]; Q[mask] *= 1.0 - p[j]; cnt[mask]++; } } } for (int i = 0; i < n; i++) { sum[0][i] = 0; for (int j = 0; j < m; j++) { sum[1 << j][i] = happiness[j][i]; } for (int mask = 0; mask < cntState; mask++) { int lowbit = mask & -mask; sum[mask][i] = sum[mask ^ lowbit][i] + sum[lowbit][i]; } } for (int mask = 0; mask < cntState; mask++) { f[mask] = sum[mask][n - 1]; } for (int i = n - 2; i >= 0; i--) { for (int mask = 1; mask < cntState; mask++) { g[mask] = P[mask] / Q[mask] * f[mask]; h[mask] = P[mask] / Q[mask] * cnt[mask] * sum[mask][i + 1]; } for (int bit = 0; bit < m; bit++) { for (int mask = cntState - 1; mask; mask--) { if ((mask >> bit) & 1) { g[mask] = g[mask] + g[mask ^ (1 << bit)]; h[mask] = h[mask] + h[mask ^ (1 << bit)]; } } } for (int mask = 1; mask < cntState; mask++) { f[mask] = Q[mask] * g[mask] + Q[mask] / cnt[mask] * h[mask] + sum[mask][i]; } } printf("%.10f\n", f[cntState - 1]); } return 0; }
-------------------------------------------------另一种姿势-------------------------------------------------
分析
期望的线性性
膜拜了PKU的代码
根据期望的线性性,分别求每个人每天的值的期望
设f[i][j][x][y]表示第i天,前j个人,前一天x,当前天y的概率
设g[i][j][x][y]表示第i天,前j个人,前一天x,当前天y的期望
那么
f[i][j][x][y]=f[i][j-1][x][y]*(1-p[j]^(i-2))+f[i][j-1][x-1][y]*p[j]^(i-2)*(1-p[j])+f[i][j-1][x-1][y-1]*p[j]^(i-1)
g[i][j][x][y]=g[i][j-1][x][y]*(1-p[j]^(i-2))+g[i][j-1][x-1][y]*p[j]^(i-2)*(1-p[j])+(g[i][j-1][x-1][y-1]+f[i][j-1][x-1][y-1]*h[j][i])*p[j]^(i-1)
递推即可,时间复杂度O(NM^3),比上述方法更优
代码
/************************************************** * Problem: HDU 5013 * Author: clavichord93 * State: Accepted、 **************************************************/ #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> using namespace std; const int MAX_N = 20; int n, m; double p[MAX_N][MAX_N]; double h[MAX_N][MAX_N]; double f[MAX_N][MAX_N][MAX_N]; double g[MAX_N][MAX_N][MAX_N]; int main() { #ifdef LOCAL_JUDGE freopen("in.txt", "r", stdin); #endif while (scanf("%d %d", &m, &n) != EOF) { for (int i = 1; i <= m; i++) { scanf("%lf", &p[i][1]); p[i][0] = 1.0; for (int j = 2; j <= n; j++) { p[i][j] = p[i][j - 1] * p[i][1]; } } for (int i = 1; i <= m; i++) { for (int j = 1; j <= n; j++) { scanf("%lf", &h[i][j]); } } memset(f, 0, sizeof(f)); memset(g, 0, sizeof(g)); double ans = 0; for (int i = 2; i <= n; i++) { f[i][0][0] = 1; for (int j = 1; j <= m; j++) { for (int x = m; x >= 0; x--) { for (int y = x; y >= 0; y--) { f[i][x][y] *= (1.0 - p[j][i - 2]); if (x > 0) { f[i][x][y] += f[i][x - 1][y] * p[j][i - 2] * (1.0 - p[j][1]); if (y > 0) { f[i][x][y] += f[i][x - 1][y - 1] * p[j][i - 1]; } } g[i][x][y] *= (1.0 - p[j][i - 2]); if (x > 0) { g[i][x][y] += g[i][x - 1][y] * p[j][i - 2] * (1.0 - p[j][1]); if (y > 0) { g[i][x][y] += (g[i][x - 1][y - 1] + f[i][x - 1][y - 1] * h[j][i]) * p[j][i - 1]; } } } } } for (int x = 1; x <= m; x++) { for (int y = 1; y <= x; y++) { ans += g[i][x][y] * ((double)y / x + 1.0); } } } for (int i = 1; i <= m; i++) { ans += h[i][1]; } printf("%.10f\n", ans); } return 0; }