嘘~ 正在从服务器偷取页面 . . .

洛谷P1850 [NOIP2016 提高组] 换教室 题解


洛谷P1850 [NOIP2016 提高组] 换教室 题解

题目链接:P1850 [NOIP2016 提高组] 换教室

题意

对于刚上大学的牛牛来说,他面临的第一个问题是如何根据实际情况申请合适的课程。

在可以选择的课程中,有 \(2n\) 节课程安排在 \(n\) 个时间段上。在第 \(i\)\(1 \leq i \leq n\))个时间段上,两节内容相同的课程同时在不同的地点进行,其中,牛牛预先被安排在教室 \(c_i\) 上课,而另一节课程在教室 \(d_i\) 进行。

在不提交任何申请的情况下,学生们需要按时间段的顺序依次完成所有的 \(n\) 节安排好的课程。如果学生想更换第 \(i\) 节课程的教室,则需要提出申请。若申请通过,学生就可以在第 \(i\) 个时间段去教室 \(d_i\) 上课,否则仍然在教室 \(c_i\) 上课。

由于更换教室的需求太多,申请不一定能获得通过。通过计算,牛牛发现申请更换第 \(i\) 节课程的教室时,申请被通过的概率是一个已知的实数 \(k_i\),并且对于不同课程的申请,被通过的概率是互相独立的。

学校规定,所有的申请只能在学期开始前一次性提交,并且每个人只能选择至多 \(m\) 节课程进行申请。这意味着牛牛必须一次性决定是否申请更换每节课的教室,而不能根据某些课程的申请结果来决定其他课程是否申请;牛牛可以申请自己最希望更换教室的 \(m\) 门课程,也可以不用完这 \(m\) 个申请的机会,甚至可以一门课程都不申请。

因为不同的课程可能会被安排在不同的教室进行,所以牛牛需要利用课间时间从一间教室赶到另一间教室。

牛牛所在的大学有 \(v\) 个教室,有 \(e\) 条道路。每条道路连接两间教室,并且是可以双向通行的。由于道路的长度和拥堵程度不同,通过不同的道路耗费的体力可能会有所不同。 当第 \(i\)\(1 \leq i \leq n-1\))节课结束后,牛牛就会从这节课的教室出发,选择一条耗费体力最少的路径前往下一节课的教室。

现在牛牛想知道,申请哪几门课程可以使他因在教室间移动耗费的体力值的总和的期望值最小,请你帮他求出这个最小值。

输入格式

第一行四个整数 \(n,m,v,e\)\(n\) 表示这个学期内的时间段的数量;\(m\) 表示牛牛最多可以申请更换多少节课程的教室;\(v\) 表示牛牛学校里教室的数量;\(e\)表示牛牛的学校里道路的数量。

第二行 \(n\) 个正整数,第 \(i\)\(1 \leq i \leq n\))个正整数表示 \(c_i\),即第 \(i\) 个时间段牛牛被安排上课的教室;保证 \(1 \le c_i \le v\)

第三行 \(n\) 个正整数,第 \(i\)\(1 \leq i \leq n\))个正整数表示 \(d_i\),即第 \(i\) 个时间段另一间上同样课程的教室;保证 \(1 \le d_i \le v\)

第四行 \(n\) 个实数,第 \(i\)\(1 \leq i \leq n\))个实数表示 \(k_i\),即牛牛申请在第 \(i\) 个时间段更换教室获得通过的概率。保证 \(0 \le k_i \le 1\)

接下来 \(e\) 行,每行三个正整数 \(a_j, b_j, w_j\),表示有一条双向道路连接教室 \(a_j, b_j\),通过这条道路需要耗费的体力值是 \(w_j\);保证 \(1 \le a_j, b_j \le v\)\(1 \le w_j \le 100\)

保证 \(1 \leq n \leq 2000\)\(0 \leq m \leq 2000\)\(1 \leq v \leq 300\)\(0 \leq e \leq 90000\)

保证通过学校里的道路,从任何一间教室出发,都能到达其他所有的教室。

保证输入的实数最多包含 \(3\) 位小数。

输出格式

输出一行,包含一个实数,四舍五入精确到小数点后恰好\(2\)位,表示答案。

数据范围

\(1 \le n,m \le 2\times 10^3,v\le 300,e\le \frac{v(v-1)}{2}\)

考虑期望dp。设 \(f_{i,j,0/1}\) 表示前 \(i\) 节课,用了 \(j\) 次花费,当前是否申请。

注意到 \(v\) 很小,我们可以先预处理出每个结点间的最短路。

转移没啥难度,其实。我们只要考虑所有情况就可以了,注意概率系数不要乘错了。

具体的可以看这篇,讲了所有情况。直接看代码也可以。

时间复杂度 \(\mathcal{O}(nm + v^3)\)

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define rep(i,a,b) for(int i=(a); i<=(b); i++)
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
void down(double &x, double y) { x > y ? x = y : 0; }
#define N ((int)(2e3+15))
#define M ((int)(315))

double p[N],f[N][N][2];
int n,m,V,E,c[N],d[N],g[M][M];
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cout << fixed << setprecision(2);

    cin >> n >> m >> V >> E; memset(g,0x3f,sizeof(g));
    rep(i,1,n) cin >> c[i]; rep(i,1,n) cin >> d[i]; rep(i,1,n) cin >> p[i];
    for(int i=1,u,v,w; i<=E; i++) { cin >> u >> v >> w; down(g[u][v], w); g[v][u] = g[u][v]; }
    for(int i=1; i<=V; i++) g[i][i] = g[i][0] = g[0][i] = 0;
    rep(k,1,V) rep(i,1,V) rep(j,1,V)
        down(g[i][j], g[i][k] + g[k][j]);
    rep(i,0,n) rep(j,0,m) f[i][j][0] = f[i][j][1] = INF;
    f[1][0][0] = f[1][1][1] = 0;
    for(int i=2; i<=n; i++)
    {
        f[i][0][0] = f[i-1][0][0] + g[c[i-1]][c[i]];
        for(int j=1; j<=min(i,m); j++)
        {
            down(f[i][j][0], min(
                f[i - 1][j][0] + g[c[i-1]][c[i]], 
                f[i - 1][j][1] + g[c[i-1]][c[i]] * (1 - p[i - 1]) + g[d[i-1]][c[i]] * p[i - 1]
            ));
            down(f[i][j][1], min(
                f[i - 1][j - 1][0] + g[c[i-1]][c[i]] * (1 - p[i]) + g[c[i-1]][d[i]] * p[i],
                f[i - 1][j - 1][1] + g[d[i-1]][d[i]] * p[i] * p[i - 1] 
                    + g[d[i-1]][c[i]] * p[i - 1] * (1 - p[i]) 
                    + g[c[i-1]][d[i]] * (1 - p[i - 1]) * p[i] 
                    + g[c[i-1]][c[i]] * (1 - p[i - 1]) * (1 - p[i])
            ));
        }
    }
    double res = 1e23; rep(i,0,m) down(res, min(f[n][i][0], f[n][i][1]));
    cout << res << '\n';
    return 0;
}

文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录