• [NOIP2016提高组]换教室


    题目:洛谷P1850、UOJ#262、BZOJ4720、Vijos P2005。

    题目大意:有n个时间段,第i个时间段只能在教室$c_i$上课,另一个上这门课的教室在$d_i$。现在你最多可以进行m次申请,对于第i个时间段的申请如果成功,那么就能在$d_i$教室上课,但成功率为$p_i$。且教室与教室之间用e条双向道路连接,每条路有一个耗费体力的值。问申请哪几门课程可以使因在教室间移动耗费的体力值的总和的期望值最小。

    解题思路:这是一道期望dp问题。对于每一时间段(第一时间段除外),我们可以分为这么几种情况。

    ①该时间段不申请:

    1.上时间段申请 成功/失败

    2.上时间段不申请

    ②该时间段申请:

    1.该时间段申请 成功/失败 and 上时间段申请 成功/失败

    2.上时间段不申请

    设f[i][j][0], f[i][j][1]表示当前在第i个时间段,申请了j个教室,0表示申请不通过,1表示申请通过,最小耗费体力的期望值。

    dis[i][j]表示教室i到教室j的最短路。

    综合以上几点情况,可得以下状态转移方程:

    $f(i,j,0)=min(f(i-1,j,0)+dis(c_{i-1},c_i),f(i-1,j,1)+dis(d_{i-1}+c_i)*p_{i-1}+dis(c_{i-1},c_i)*(1-p_{i-1}))$
    $f(i,j,1)=min(f(i-1,j-1,0)+dis(c_{i-1},d_i)*p_i+dis(c_{i-1},c_i)*(1-p_i),f(i-1,j-1,1)+dis(d_{i-1},d_i)*p_{i-1}*p_i+dis(d_{i-1},c_i)*p_{i-1}*(1-p_i)+dis(c_{i-1},d_i)*(1-p_{i-1})*p_i+dis(c_{i-1},c_i)*(1-p_{i-1})*(1-p_i))$

    dis用floyd求出,注意dis[i,i]=0

    那么时间复杂度为$O(v^3+nm)$。空间复杂度为$O(v^2+nm)$,可以用滚动数组优化为$O(v^2+m)$。

    C++ Code:

    #include<cstdio>
    #include<cstring>
    using namespace std;
    int n,m,v,e,c[2002],d[2002],dis[302][302];
    double p[2002],f[2][2002][2];
    template<typename T>
    inline T min(T a,T b){return(a<b)?a:b;}
    int main(){
    	scanf("%d%d%d%d",&n,&m,&v,&e);
    	memset(dis,0x3f,sizeof dis);
    	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",&p[i]);
    	while(e--){
    		int u,v,t;
    		scanf("%d%d%d",&u,&v,&t);
    		dis[u][v]=dis[v][u]=min(dis[u][v],t);
    	}
    	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)
    	if(i!=k)
    	for(int j=1;j<=v;++j)
    	if(i!=j&&j!=k)
    	dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
    	memset(f,0x7f,sizeof f);
    	f[1][0][0]=f[1][1][1]=0;
    	for(int i=2;i<=n;++i){
    		memset(f[i&1],0x7f,sizeof f[i&1]);
    		f[i&1][0][0]=f[i&1^1][0][0]+dis[c[i-1]][c[i]];
    		for(int j=1;j<=i&&j<=m;++j){
    			f[i&1][j][0]=min(f[i&1^1][j][0]+dis[c[i-1]][c[i]],
    			f[i&1^1][j][1]+dis[d[i-1]][c[i]]*p[i-1]+dis[c[i-1]][c[i]]*(1-p[i-1]));
    			f[i&1][j][1]=min(f[i&1^1][j-1][0]+dis[c[i-1]][d[i]]*p[i]+dis[c[i-1]][c[i]]*(1-p[i]),
    			f[i&1^1][j-1][1]+dis[d[i-1]][d[i]]*p[i-1]*p[i]+dis[d[i-1]][c[i]]*p[i-1]*(1-p[i])+
    			dis[c[i-1]][d[i]]*(1-p[i-1])*p[i]+dis[c[i-1]][c[i]]*(1-p[i-1])*(1-p[i]));
    		}
    	}
    	double ans=f[n&1][0][0];
    	for(int i=1;i<=m;++i){
    		ans=min(ans,f[n&1][i][0]);
    		ans=min(ans,f[n&1][i][1]);
    	}
    	printf("%.2f
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    canvas上的像素操作(图像复制,细调)
    AMD、CMD、CommonJS 和 ES6 模块化规范
    JS垃圾回收
    函数式编程中如何处理副作用?
    vue中的$on,$emit,$once,$off源码实现
    重新学习react生命周期
    keep alive实现原理
    读取 url 参数方法
    使用Bootstratp Blazor +EF Codefirst 愉快的增删改查!
    记录一下爬取微信小程序视频的过程!
  • 原文地址:https://www.cnblogs.com/Mrsrz/p/7603447.html
Copyright © 2020-2023  润新知