[HDU 5013 City Tour] 状态压缩DP+分离参量+多重DP / 期望的线性性

题目

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;
}


你可能感兴趣的:([HDU 5013 City Tour] 状态压缩DP+分离参量+多重DP / 期望的线性性)