【NOIP2016提高组】换教室

https://www.luogu.org/problem/show?pid=1850

题面很长,实质很水的一道期望DP题。题面自带劝退效果。

首先用Floyd算出任意两点的最短路径。然后设f(i,j,0)为前i节课申请更换j节,且不申请第i节时的最小期望;设f(i,j,1)前i节课申请更换j节,且申请第i节时的最小期望。
可得下面这个超长的状转方程:
f(i,j,0)=min{
  f(i-1,j,0) + dist(c[i-1],c[i]),    // 不申请第i-1节
  f(i-1,j,1) + k[i-1]*dist(d[i-1],c[i]) + (1-k[i-1])*dist(c[i-1],c[i])    // 申请第i-1节
}
f(i,j,1)=min{
  f(i-1,j-1,0) + k[i]*dist(c[i-1],d[i]) + (1-k[i])*dist(c[i-1],c[i]),    // 不申请第i-1节
  f(i-1,j-1,1) + k[i-1]*k[i]*dist(d[i-1],d[i]) + (1-k[i-1])*k[i]*dist(c[i-1],d[i]) + k[i-1]*(1-k[i])*dist(d[i-1],c[i]) + (1-k[i-1])*(1-k[i])*dist(c[i-1],c[i])    // 申请第i-1节
}
注意f(i,0,0)意味着一节课都不申请,需要特判f(i,0,0)=f(i-1,0,0)+dist(c[i-1],c[i])

#include <cstdio>
#include <algorithm>
#define maxv 310
#define maxn 2010
using namespace std;
const int inf = 1000;
int v, e; // v表示牛牛学校里教室的数量;e表示牛牛的学校里道路的数量
int dist[maxv][maxv];
void add_edge(int from, int to, int weight)
{
    dist[from][to] = min(dist[from][to], weight); //注意重边和自环
    dist[to][from] = dist[from][to];
}
void floyd()
{
    // d(k,i,j)=min{d(k-1,i,j), d(k-1,i,k)+d(k-1,k,j)}
    for (int k = 1; k <= v; k++)
        for (int i = 1; i <= v; i++)
            for (int j = 1; j <= v; j++)
                dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
}
int n, m;        // n表示这个学期内的时间段的数量;m表示牛牛最多可以申请更换多少节课程的教室
int c[maxn];    // 第i个时间段牛牛被安排上课的教室
int d[maxn];    // 第i个时间段另一间上同样课程的教室
double k[maxn]; // 牛牛申请在第i个时间段更换教室获得通过的概率
void load()
{
    scanf("%d%d%d%d", &n, &m, &v, &e);
    for (int i = 1; i <= n; i++)
        scanf("%d", &c[i]);
    for (int i = 1; i <= n; i++)
        scanf("%d", &d[i]);
    for (int i = 1; i <= n; i++)
        scanf("%lf", &k[i]);

    for (int i = 1; i <= v; i++) //初始化邻接矩阵
    {
        for (int j = 1; j <= v; j++)
            dist[i][j] = inf;
        dist[i][i] = 0;
    }

    int tmp1, tmp2, tmp3;
    for (int i = 1; i <= e; i++)
    {
        scanf("%d%d%d", &tmp1, &tmp2, &tmp3);
        add_edge(tmp1, tmp2, tmp3);
    }
}
double dp[maxn][maxn][2];
int main()
{
    load();
    floyd();
    for (int i = 1; i <= n; i++)
    {
        dp[i][0][0] = dp[i - 1][0][0] + dist[c[i - 1]][c[i]]; // f(i,0,0)意味着一节课都不申请,需要特判
        dp[i][0][1] = 2000000;
        for (int j = 1; j <= m; j++)
        {
            // 第1~i节课,申请更换j节,且不申请第i节时的最小期望
            dp[i][j][0] = min(
                dp[i - 1][j][0] + dist[c[i - 1]][c[i]],                                                      // 不申请第i-1节
                dp[i - 1][j][1] + k[i - 1] * dist[d[i - 1]][c[i]] + (1 - k[i - 1]) * dist[c[i - 1]][c[i]] // 申请第i-1节
                );

            // 第1~i节课,申请更换j节,且申请第i节时的最小期望
            dp[i][j][1] = min(
                dp[i - 1][j - 1][0] + k[i] * dist[c[i - 1]][d[i]] + (1 - k[i]) * dist[c[i - 1]][c[i]],                                                                                                                            // 不申请第i-1节
                dp[i - 1][j - 1][1] + k[i - 1] * k[i] * dist[d[i - 1]][d[i]] + (1 - k[i - 1]) * k[i] * dist[c[i - 1]][d[i]] + k[i - 1] * (1 - k[i]) * dist[d[i - 1]][c[i]] + (1 - k[i - 1]) * (1 - k[i]) * dist[c[i - 1]][c[i]] // 申请第i-1节
                );
        }
    }
    printf("%.2f", min(dp[n][m][0], dp[n][m][1]));
    return 0;
}

 

posted @ 2017-09-16 11:39  ssttkkl  阅读(306)  评论(0编辑  收藏  举报