洛谷 P1850 换教室 题解

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了洛谷 P1850 换教室 题解相关的知识,希望对你有一定的参考价值。

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置。

题目链接:https://www.luogu.org/problem/show?pid=1850

题目描述

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

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

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

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

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

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

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

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

 

输入输出格式

输入格式:

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

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

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

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

接下来 e 行,每行三个正整数 aj,bj,w?,表示有一条双向道路连接教室 aj,bj,通过这条道路需要耗费的体力值是 w?;保证 1≤aj,bj≤v,1≤wj≤100

保证 1≤n≤2000,0≤m≤20000,1≤v≤300,0≤e≤900000。

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

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

 

输出格式:

输出一行,包含一个实数,四舍五入精确到小数点后恰好2位,表示答案。你的输出必须和标准输出完全一样才算正确。

测试数据保证四舍五入后的答案和准确答案的差的绝对值不大于 4*10^−3。 (如果你不知道什么是浮点误差,这段话可以理解为:对于大多数的算法,你可以正常地使用浮点数类型而不用对它进行特殊的处理)

输入输出样例

输入样例#1: 
3 2 3 3
2 1 2
1 2 1
0.8 0.2 0.5 
1 2 5
1 3 3
2 3 1
输出样例#1: 
2.80

说明

【样例1说明】

所有可行的申请方案和期望收益如下表:

技术分享

【提示】

1.道路中可能会有多条双向道路连接相同的两间教室。 也有可能有道路两端连接

的是同一间教室。

2.请注意区分n,m,v,e的意义, n不是教室的数量, m不是道路的数量。

技术分享

特殊性质1:图上任意两点 ai, bi?, ai?≠ bi?间,存在一条耗费体力最少的路径只包含一条道路。

特殊性质2:对于所有的 1≤ i≤ n,ki=1。

 

分析:

状态还是比较好想的,dp[i][j][k]表示当前是第i节课,到现在为止申请了j次,第i节申请/不申请换教室(k的值为1/0)的最短期望距离。

每次更新距离时只与这两节课是否申请更换教室有关,所以只需要考虑第i-1节课和第i节课是否申请,以及申请是否成功。

第i节课不申请:

1.第i-1节课不申请  那么这两个教室的距离就是原本的距离

dp[i][j][0] = dp[i-1][j][0] + dis[c[i-1]][c[i]]

2.第i-1节课申请 申请可能成功/失败,求期望距离

dp[i][j][0] = dp[i-1][j][1] + k[i-1]*dis[d[i-1]][c[i]] + (1-k[i-1])*dis[c[i-1]][c[i]]

第i节课申请:

1.第i-1节课不申请 第i节的申请可能成功/失败,求期望距离

dp[i][j][1] = dp[i-1][j-1][0] + k[i]*dis[c[i-1]][d[i]] + (1-k[i])*dis[c[i-1]][c[i]]

2.第i-1节课也申请,这两次申请都可能成功/失败,所以一共有四种情况

dp[i][j][1] = dp[i-1][j-1][1] +

k[i-1]*k[i]*dis[d[i-1]][d[i]] +

k[i-1]*(1-k[i])*dis[d[i-1]][c[i]] +

(1-k[i-1])*k[i]*dis[c[i-1]][d[i]] +

(1-k[i-1])*(1-k[i])*dis[c[i-1]][c[i]]

每种情况取min值更新,ans = Min(dp[n][j][k])。整体来说难度不大。

如果你选择手动赋初值的话,注意dp数组需要从[i = 1,j = 0]开始更新!

当然还是建议用memset(dp,127,sizeof(dp)).感谢某位好心的dalao帮忙改错。

 

AC代码:

 

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<cmath>
 5 
 6 const int INF = 1e9;
 7 inline void read(int &x)
 8 {
 9     char ch = getchar(),c = ch;x = 0;
10     while(ch < 0 || ch > 9) c = ch,ch = getchar();
11     while(ch <= 9 && ch >= 0) x = (x<<1)+(x<<3)+ch-0,ch = getchar();
12     if(c == -) x = -x;
13 }
14 
15 int n,m,v,e,f,t,tmp;
16 //n:课程数  m:申请上限  v:教室数  e:路径数 
17 int c[2002],d[2002],G[500][500];
18 double k[2002],dp[2002][2002][2],ans;
19 
20 inline int Min(int a,int b)
21 {return a<b?a:b;}
22 
23 inline double Min(double a,double b)
24 {return a<b?a:b;}
25 
26 void init()
27 {
28     for(int i = 1;i <= v;++ i)
29         for(int j = 1;j <= v;++ j)
30             if(i == j) G[i][j] = 0;
31             else G[i][j] = INF;
32             
33     for(int i = 1;i <= n;++ i)
34         for(int j = 0;j <= n;++ j)
35             dp[i][j][0] = dp[i][j][1] = INF;
36     dp[1][0][0] = 0,dp[1][1][1] = 0;
37 }
38 
39 void Floyd()
40 {
41     for(int k = 1;k <= v;++ k)
42         for(int i = 1;i <= v;++ i)
43             for(int j = 1;j <= v;++ j)
44                 G[i][j] = Min(G[i][j],G[i][k]+G[k][j]);
45 }
46 
47 int main()
48 {
49 //    freopen("1.txt","r",stdin);
50     read(n),read(m),read(v),read(e);
51     for(register int i = 1;i <= n;++ i)
52         read(c[i]);
53     for(register int i = 1;i <= n;++ i)
54         read(d[i]);
55     for(register int i = 1;i <= n;++ i)
56         scanf("%lf",&k[i]);
57     init();
58     for(register int i = 1;i <= e;++ i){
59         read(f),read(t),read(tmp);
60         if(f == t || tmp > G[f][t]) continue;
61         G[f][t] = G[t][f] = tmp;
62     }
63     Floyd();
64 
65     for(int i = 2;i <= n;++ i)
66     {
67         dp[i][0][0] = dp[i-1][0][0]+G[c[i-1]][c[i]];
68         for(int j = 1;j <= Min(i,m);++ j)
69         {
70             dp[i][j][0] = Min(dp[i-1][j][0]+G[c[i-1]][c[i]],
71             dp[i-1][j][1]+k[i-1]*G[d[i-1]][c[i]]+(1-k[i-1])*G[c[i-1]][c[i]]);
72             dp[i][j][1] = Min(dp[i-1][j-1][0] + k[i]*G[c[i-1]][d[i]] + (1-k[i])*G[c[i-1]][c[i]],
73             dp[i-1][j-1][1]+k[i-1]*k[i]*G[d[i-1]][d[i]]+
74             k[i-1]*(1-k[i])*G[d[i-1]][c[i]]+
75             (1-k[i-1])*k[i]*G[c[i-1]][d[i]]+
76             (1-k[i-1])*(1-k[i])*G[c[i-1]][c[i]]);
77         }
78     }
79     ans = dp[n][0][0];
80     for(int i = 1;i <= m;++ i)
81         ans = Min(ans,Min(dp[n][i][1],dp[n][i][0]));
82     printf("%.2lf\n",ans);
83     return 0;
84 }

 

以上是关于洛谷 P1850 换教室 题解的主要内容,如果未能解决你的问题,请参考以下文章

洛谷P1850 换教室

洛谷P1850换教室

AC日记——换教室 洛谷 P1850

洛谷 P1850 换教室(NOIp2016提高组D1T3)

P1850 换教室

做题记录:P1850 换教室