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

PS:如果读过题了可以跳过题目描述直接到题解部分
提交链接:洛谷 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说明】

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

【提示】

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

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

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

题解

Floyd

求最短路的一种方法,效率 O ( n 3 ) O(n^3) O(n3)
循环每次的中途必经点,再循环起点和终点,每次取直接起点到终点和经过中点两种情况值更小的一种即可。

DP

每次分四种情况转移:

  1. 当前选或不选
  2. 前一个选或不选

分别取最小值即可。
最后从1-m选或不选共2*m种情况中取最小值。

代码实现

//洛谷 P1850 [NOIP2016 提高组] 换教室
#pragma GCC optimize(3)
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n;//时间段的数量
int m;//最多可以申请更换多少节课程的教室
int v;//学校里教室的数量
int e;//学校里道路的数量
int c[2010];//被安排的教室
int d[2010];//另一间教室
double k[2010];//申请通过的概率
int a;//道路连接的教室
int b;//道路连接的另一个教室
int w[2010][2010];//耗费的体力 
double f[2010][2010][2];//f[i][j][1/0] 第i节课,已换j次,换或不换 
double ans;

int main(){
	register int i,j,l;
	scanf("%d%d%d%d",&n,&m,&v,&e);
	for(i=1;i<=n;++i){
		scanf("%d",&c[i]);
	}
	for(i=1;i<=n;++i){
		scanf("%d",&d[i]);
	}
	for(i=1;i<=n;++i){
		scanf("%lf",&k[i]);
	}
	memset(w,999999,sizeof(w));
	memset(f,9999999,sizeof(f));
	for(i=1;i<=e;++i){
		scanf("%d%d",&a,&b);
		int z;
		scanf("%d",&z);
		w[b][a]=w[a][b]=min(w[a][b],z);
	}
	for(i=1;i<=v;++i){
		w[i][i]=0;
	}
	for(i=1;i<=v;++i){//floyd
		for(j=1;j<=v;++j){
			for(l=1;l<j;++l){
				if(w[j][i]+w[i][l]<w[j][l]){
					w[l][j]=w[j][l]=w[j][i]+w[i][l];
				}
			}
		}
	}
	f[1][0][0]=f[1][1][1]=0;
	for(i=2;i<=n;++i){
		for(j=0;j<=min(m,i);++j){
			f[i][j][0]=min(f[i-1][j][0]+w[c[i-1]][c[i]],f[i-1][j][1]+w[d[i-1]][c[i]]*1.0*k[i-1]+w[c[i-1]][c[i]]*1.0*(1-k[i-1]));
			if(j>0){
				f[i][j][1]=min(f[i-1][j-1][0]+w[c[i-1]][d[i]]*1.0*k[i]+w[c[i-1]][c[i]]*1.0*(1-k[i]),f[i-1][j-1][1]+w[d[i-1]][d[i]]*1.0*k[i-1]*k[i]+w[c[i-1]][d[i]]*1.0*(1-k[i-1])*k[i]+w[d[i-1]][c[i]]*1.0*k[i-1]*(1-k[i])+w[c[i-1]][c[i]]*(1-k[i-1])*(1-k[i]));
			}
		} 
	}
	ans=2e9;
	for(i=0;i<=m;++i){
		ans=min(ans,min(f[n][i][0],f[n][i][1]));
	}
	printf("%.2lf\n",ans);
	return 0;
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

月半流苏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值