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

P1850 [NOIP2016 提高组] 换教室

题面:

题目描述

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

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

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

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

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

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

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

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

输入格式

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

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

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

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

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

保证 1 ≤ n ≤ 2000 1 \leq n \leq 2000 1n2000 0 ≤ m ≤ 2000 0 \leq m \leq 2000 0m2000 1 ≤ v ≤ 300 1 \leq v \leq 300 1v300 0 ≤ e ≤ 90000 0 \leq e \leq 90000 0e90000

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

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

输出格式

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

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

样例 #1

样例输入 #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 说明

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

  • 不作申请,耗费的体力值的期望为 8.0 8.0 8.0
申请通过的时间段出现的概率耗费的体力值
1.0 1.0 1.0 8 8 8
  • 申请更换第 1 1 1 个时间段的上课教室,耗费的体力值的期望为 4.8 4.8 4.8
申请通过的时间段出现的概率耗费的体力值
1 1 1 0.8 0.8 0.8 4 4 4
0.2 0.2 0.2 8 8 8
  • 申请更换第 2 2 2 个时间段的上课教室,耗费的体力值的期望为 6.4 6.4 6.4
申请通过的时间段出现的概率耗费的体力值
2 2 2 0.2 0.2 0.2 0 0 0
0.8 0.8 0.8 8 8 8
  • 申请更换第 3 3 3 个时间段的上课教室,耗费的体力值的期望为 6.0 6.0 6.0
申请通过的时间段出现的概率耗费的体力值
3 3 3 0.5 0.5 0.5 4 4 4
0.5 0.5 0.5 8 8 8
  • 申请更换第 1 , 2 1,2 1,2 个时间段的上课教室,耗费的体力值的期望为 4.48 4.48 4.48
申请通过的时间段出现的概率耗费的体力值
1 , 2 1,2 1,2 0.16 0.16 0.16 4 4 4
1 1 1 0.64 0.64 0.64 4 4 4
2 2 2 0.04 0.04 0.04 0 0 0
0.16 0.16 0.16 8 8 8
  • 申请更换第 1 , 3 1,3 1,3 个时间段的上课教室,耗费的体力值的期望为 2.8 2.8 2.8
申请通过的时间段出现的概率耗费的体力值
1 , 3 1,3 1,3 0.4 0.4 0.4 0 0 0
1 1 1 0.4 0.4 0.4 4 4 4
3 3 3 0.1 0.1 0.1 4 4 4
0.1 0.1 0.1 8 8 8
  • 申请更换第 2 , 3 2,3 2,3 个时间段的上课教室,耗费的体力值的期望为 5.2 5.2 5.2
申请通过的时间段出现的概率耗费的体力值
2 , 3 2,3 2,3 0.1 0.1 0.1 4 4 4
2 2 2 0.1 0.1 0.1 0 0 0
3 3 3 0.4 0.4 0.4 4 4 4
0.4 0.4 0.4 8 8 8

因此,最优方案为:申请更换第 1 , 3 1,3 1,3 个时间段的上课教室。耗费的体力值的期望为 2.8 2.8 2.8

提示

  1. 道路中可能会有多条双向道路连接相同的两间教室。 也有可能有道路两端连接的是同一间教室。
  2. 请注意区分 n , m , v , e n,m,v,e n,m,v,e 的意义, n n n 不是教室的数量, m m m 不是道路的数量。

数据范围与说明

测试点编号 n ≤ n\le n m ≤ m\le m v ≤ v\le v是否具有特殊性质 1是否具有特殊性质 2
1 1 1 1 1 1 1 300 300 300 × \times × × \times ×
2 2 2 2 0 0 0 20 20 20 × \times × × \times ×
3 2 2 2 1 1 1 100 100 100 × \times × × \times ×
4 2 2 2 2 2 2 300 300 300 × \times × × \times ×
5 3 3 3 0 0 0 20 20 20 √ \surd √ \surd
6 3 3 3 1 1 1 100 100 100 √ \surd × \times ×
7 3 3 3 2 2 2 300 300 300 × \times × × \times ×
8 10 10 10 0 0 0 300 300 300 √ \surd √ \surd
9 10 10 10 1 1 1 20 20 20 √ \surd × \times ×
10 10 10 10 2 2 2 100 100 100 × \times × × \times ×
11 10 10 10 10 10 10 300 300 300 × \times × √ \surd
12 20 20 20 0 0 0 20 20 20 √ \surd × \times ×
13 20 20 20 1 1 1 100 100 100 × \times × × \times ×
14 20 20 20 2 2 2 300 300 300 √ \surd × \times ×
15 20 20 20 20 20 20 300 300 300 × \times × √ \surd
16 300 300 300 0 0 0 20 20 20 × \times × × \times ×
17 300 300 300 1 1 1 100 100 100 × \times × × \times ×
18 300 300 300 2 2 2 300 300 300 √ \surd √ \surd
19 300 300 300 300 300 300 300 300 300 × \times × √ \surd
20 2000 2000 2000 0 0 0 20 20 20 × \times × × \times ×
21 2000 2000 2000 1 1 1 20 20 20 × \times × × \times ×
22 2000 2000 2000 2 2 2 100 100 100 × \times × × \times ×
23 2000 2000 2000 2000 2000 2000 100 100 100 × \times × × \times ×
24 2000 2000 2000 2000 2000 2000 300 300 300 × \times × × \times ×
25 2000 2000 2000 2000 2000 2000 300 300 300 × \times × × \times ×

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

特殊性质 2:对于所有的 1 ≤ i ≤ n ,   k i = 1 1≤ i≤ n,\ k_i= 1 1in, ki=1

换,还是不换?这由 d p dp dp 说的算!

V ∈ [ 20 , 300 ] V \in [20,300] V[20,300] 果断 floyd

求出 d i s dis dis 数组,然后考虑设计状态(为表示方便,后面用 g g g 表示最短路)

无非 换不换 的问题,用 [ 0 / 1 ] [0/1] [0/1] 表示

换的个数有限制,用 [ 0 ∼ m ] [0 \sim m] [0m] 描述

总课程有限,用 [ 0 ∼ n ] [0\sim n] [0n] 描述

所以 d p dp dp 状态为 f i : 0 ∼ n , j : 0 ∼ m , 0 / 1 f_{i:0\sim n,j:0\sim m,0/1} fi:0n,j:0m,0/1,表示 前 i i i 次课程,已经换了 j j j 次,本次换没换的期望值

考虑转移!

本次不换的转移:
f i , j , 0 = min ⁡ { f i − 1 , j , 0 + g c i , c i − 1 f i − 1 , j , 1 + g c i , c i − 1 ( 1 − k i − 1 ) + g c i , d i − 1 k i − 1 f_{i,j,0} = \min \begin{cases} f_{i - 1,j,0} + g_{c_i,c_{i - 1}} \\ f_{i - 1,j,1} + g_{c_i,c_{i - 1}}(1 - k_{i - 1}) + g_{c_i,d_{i - 1}}k_{i - 1} \\ \end{cases} fi,j,0=min{fi1,j,0+gci,ci1fi1,j,1+gci,ci1(1ki1)+gci,di1ki1

本次换的转移:
f i , j , 1 = min ⁡ { f i − 1 , j − 1 , 0 + g c i , c i − 1 ( 1 − k i ) + g d i , c i − 1 k i f i − 1 , j − 1 , 1 + g c i , c i − 1 ( 1 − k i − 1 ) ( 1 − k i ) + g c i , d i − 1 k i − 1 ( 1 − k i ) + g d i , c i − 1 ( 1 − k i − 1 ) k i + g d i , d i − 1 k i k i − 1 f_{i,j,1} = \min \begin{cases} f_{i - 1,j - 1,0} + g_{c_i,c_{i-1}}(1 - k_i) + g_{d_i,c_{i - 1}}k_i \\ f_{i - 1,j - 1,1} + g_{c_i,c_{i - 1}}(1 - k_{i - 1})(1 - k_i) + g_{c_i,d_{i - 1}}k_{i - 1}(1 - k_i) + g_{d_i,c_{i - 1}}(1 - k_{i - 1})k_i + g_{d_i,d_{i - 1}}k_ik_{i - 1} \end{cases} fi,j,1=min{fi1,j1,0+gci,ci1(1ki)+gdi,ci1kifi1,j1,1+gci,ci1(1ki1)(1ki)+gci,di1ki1(1ki)+gdi,ci1(1ki1)ki+gdi,di1kiki1

注意初始化, f 1 , 0 , 0 = f 1 , 1 , 1 = 0 f_{1,0,0} = f_{1,1,1} = 0 f1,0,0=f1,1,1=0

AC-code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
int rd() {
	int x = 0, w = 1;
	char ch = 0;
	while (ch < '0' || ch > '9') {
		if (ch == '-') w = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = x * 10 + (ch - '0');
		ch = getchar();
	}
	return x * w;
}

void wt(int x) {
	static int sta[35];
	int f = 1;
	if(x < 0) f = -1,x *= f;
	int top = 0;
	do {
		sta[top++] = x % 10, x /= 10;
	} while (x);
	if(f == -1) putchar('-');
	while (top) putchar(sta[--top] + 48);
}
const int V = 305,N = 2005;
int n,m,v,e;
int dis[V][V];
int c[N],d[N];
double k[N];
double f[N][N][2];

signed main() {
	memset(dis,0x3f,sizeof(dis));
	n = rd(),m = rd(),v = rd(),e = rd();
	for(int i = 1;i<=n;i++) c[i] = rd();
	for(int i = 1;i<=n;i++) d[i] = rd();
	for(int i = 1;i<=n;i++) scanf("%lf",&k[i]);
	for(int i = 1;i<=e;i++) {
		int u = rd(),v = rd(),w = rd();
		dis[u][v] = dis[v][u] = min(dis[u][v],w);
	}
	for(int i = 1;i<=v;i++) dis[i][i] = 0;
	for(int k = 1;k<=v;k++)
		for(int i = 1;i<=v;i++)
			for(int j = 1;j<=v;j++)
				dis[i][j] = min(dis[i][j],dis[i][k] + dis[k][j]);
	for(int i = 1;i<=v;i++) dis[i][0] = dis[0][i] = 0;
	for(int i = 0;i<=n;i++)
		for(int j = 0;j<=m;j++)
			f[i][j][0] = f[i][j][1] = 1e18;
	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] + dis[c[i - 1]][c[i]];
		for(int j = 1;j<=min(i,m);j++) {
			f[i][j][0] = min(f[i][j][0],f[i - 1][j][0] + dis[c[i - 1]][c[i]]);
			f[i][j][0] = min(f[i][j][0],f[i - 1][j][1] + dis[c[i - 1]][c[i]] * (1 - k[i - 1]) + dis[d[i - 1]][c[i]] * k[i - 1]);
			f[i][j][1] = min(f[i][j][1],(f[i - 1][j - 1][0]) + dis[c[i - 1]][c[i]] * (1 - k[i]) + dis[c[i - 1]][d[i]] * k[i]);
			f[i][j][1] = min(f[i][j][1],(f[i - 1][j - 1][1]) + dis[c[i - 1]][c[i]] * (1 - k[i - 1]) * (1 - k[i]) + dis[d[i - 1]][c[i]] * k[i - 1] * (1 - k[i]) + dis[c[i - 1]][d[i]] * (1 - k[i - 1]) * k[i] + dis[d[i - 1]][d[i]] * k[i - 1] * k[i]);
		}
	}
	double ans = (double)(1e18);
	for(int i = 0;i<=m;i++) ans = min(ans,min(f[n][i][0],f[n][i][1]));
	printf("%.2lf",ans);
	return 0;
}
/*
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

*/
  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值