P1850 换教室

终于认识了什么叫做期望。。

这道题题目挺长的哈,看上去又是期望又是图论的样子。

但是图论方面只需要做个预处理,然后就可以用dp弄进答案。

在教室之间移动,有脑子的都不会走弯路更累吧,所以最短路走一波。

然后就是dp的事情了。

\(dp[i][j][0/1]\)是第\(i\)个时间段,从开始到现在(包含现在)已经申请了\(j\)次,要去往原教室(0)或新教室(1)上课的期望。

这道题的第一次去上课的路径是白送你的,具体看样例的解释就知道了。

如何算期望?一般使用全期望公式,就是各种情况的概率乘以相应的值的和,类似于加权平均数的算法。

所以考虑一下状态转移方程:

这些东西的转移都可能来自上次换了或者上次没换。

然后重点来了:上次没换这种情况还要分为两种:换了成功了或者换了但没成功。对于这些情况,算期望的时候要全部都考虑在内,然后算进去,才是正确的答案。

\(dp[i][j][0]\)可能由上次没换得来,直接在原来的状态基础上加上这段路径即可。

但也可能由上次换了得来,设上次换成功的概率为\(k\),则我们应该加上\(k \times G[s_1][t_0] + (1 - k) \times G[s_0][t_0]\)

\(dp[i][j][1]\)就复杂了。

先考虑由上次不换转移来。同理应该在原来的基础上加上\(k \times G[s_0][t_1] + (1 - k) \times G[s_0][t_0]\),这里的\(k\)是当前成功的概率。

如果是上次不换,由乘法原理,就会有4种情况,讲道理自己推一下,并不难,但太长。

细节方面可以预处理一波边界问题,不然可能递推的时候很难受。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
const int maxn = 2005, maxv = 305;
const double INF = 999999999;
int c[maxn][2];
double p[maxn], dp[maxn][maxn][2];
int G[maxn][maxn];
int n, m, v, e;

int main()
{
    scanf("%d%d%d%d", &n, &m, &v, &e);
    for(int i = 1; i <= n; i++) scanf("%d", &c[i][0]);
    for(int i = 1; i <= n; i++) scanf("%d", &c[i][1]);
    for(int i = 1; i <= n; i++) scanf("%lf", &p[i]);
    memset(G, 0x3f, sizeof G);
    for(int i = 1; i <= v; i++) G[i][i] = 0;
    while(e--)
    {
        int u, v, w; scanf("%d%d%d", &u, &v, &w);
        G[u][v] = G[v][u] = std::min(G[u][v], w);
    }
    for(int k = 1; k <= v; k++)
        for(int i = 1; i <= v; i++)
            for(int j = 1; j <= v; j++)
                G[i][j] = std::min(G[i][j], G[i][k] + G[k][j]);
    for(int i = 1; i <= n; i++)
        for(int j = 0; j <= m; j++)
            dp[i][j][0] = dp[i][j][1] = INF;
    dp[1][0][0] = dp[1][1][1] = dp[1][1][0] = 0;
    for(int i = 2; i <= n; i++) dp[i][0][0] = dp[i - 1][0][0] + G[c[i - 1][0]][c[i][0]];
    for(int i = 2; i <= n; i++)
    {
        int s0 = c[i - 1][0], s1 = c[i - 1][1];
        int t0 = c[i][0], t1 = c[i][1];
        for(int j = 1; j <= std::min(i, m); j++)
        {
            dp[i][j][0] = std::min(dp[i][j][0], std::min(dp[i - 1][j][0] + G[s0][t0], dp[i - 1][j][1] + p[i - 1] * G[s1][t0] + (1 - p[i - 1]) * G[s0][t0]));
            dp[i][j][1] = std::min(dp[i][j][1], std::min(dp[i - 1][j - 1][0] + p[i] * G[s0][t1] + (1 - p[i]) * G[s0][t0], dp[i - 1][j - 1][1] + p[i - 1] * p[i] * G[s1][t1] + p[i - 1] * (1 - p[i]) * G[s1][t0] + (1 - p[i - 1]) * p[i] * G[s0][t1] + (1 - p[i - 1]) * (1 - p[i]) * G[s0][t0]));
        }
    }
    double ans = INF;
    for(int i = 0; i <= m; i++) ans = std::min(ans, std::min(dp[n][i][0], dp[n][i][1]));
    printf("%.2lf\n", ans);
    return 0;
}
posted @ 2018-08-29 21:16  Garen-Wang  阅读(163)  评论(0编辑  收藏  举报