zoukankan      html  css  js  c++  java
  • UOJ262 【NOIP2016】换教室

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

     

    本文作者:ljh2000
    作者博客:http://www.cnblogs.com/ljh2000-jump/
    转载请注明出处,侵权必究,保留最终解释权!

     

     

    Description

    对于刚上大学的牛牛来说,他面临的第一个问题是如何根据实际情况申请合适的课程。在可以选择的课程中,有2n节
    课程安排在n个时间段上。在第i(1≤i≤n)个时间段上,两节内容相同的课程同时在不同的地点进行,其中,牛牛预先
    被安排在教室ci上课,而另一节课程在教室di进行。在不提交任何申请的情况下,学生们需要按时间段的顺序依次完
    成所有的n节安排好的课程。如果学生想更换第i节课程的教室,则需要提出申请。若申请通过,学生就可以在第i个
    时间段去教室di上课,否则仍然在教室ci上课。由于更换教室的需求太多,申请不一定能获得通过。通过计算,牛牛
    发现申请更换第i节课程的教室时,申请被通过的概率是一个已知的实数ki,并且对于不同课程的申请,被通过的概率
    是互相独立的。学校规定,所有的申请只能在学期开始前一次性提交,并且每个人只能选择至多m节课程进行申请。
    这意味着牛牛必须一次性决定是否申请更换每节课的教室,而不能根据某些课程的申请结果来决定其他课程是否申
    请;牛牛可以申请自己最希望更换教室的m门课程,也可以不用完这m个申请的机会,甚至可以一门课程都不申请。因
    为不同的课程可能会被安排在不同的教室进行,所以牛牛需要利用课间时间从一间教室赶到另一间教室。牛牛所在
    的大学有v个教室,有e条道路。每条道路连接两间教室,并且是可以双向通行的。由于道路的长度和拥堵程度不同,
    通过不同的道路耗费的体力可能会有所不同。当第i(1≤i≤n-1)节课结束后,牛牛就会从这节课的教室出发,选择一
    条耗费体力最少的路径前往下一节课的教室。现在牛牛想知道,申请哪几门课程可以使他因在教室间移动耗费的体
    力值的总和的期望值最小,请你帮他求出这个最小值。

    Input

    第一行四个整数n,m,v,e。n表示这个学期内的时间段的数量;m表示牛牛最多可以申请更换多少节课程的教室;
    v表示牛牛学校里教室的数量;e表示牛牛的学校里道路的数量。
    第二行n个正整数,第i(1≤i≤n)个正整数表示c,,即第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,wj,表示有一条双向道路连接教室aj,bj,通过这条道路需要耗费的体力值是Wj;
    保证1≤aj,bj≤v,1≤wj≤100。
    保证1≤n≤2000,0≤m≤2000,1≤v≤300,0≤e≤90000。
    保证通过学校里的道路,从任何一间教室出发,都能到达其他所有的教室。
    保证输入的实数最多包含3位小数。
     

    Output

    输出一行,包含一个实数,四舎五入精确到小数点后恰好2位,表示答案。你的
    输出必须和标准输出完全一样才算正确。
    测试数据保证四舎五入后的答案和准确答案的差的绝对值不大于4*10^-3。(如果你不知道什么是浮点误差,这段话
    可以理解为:对于大多数的算法,你可以正常地使用浮点数类型而不用对它进行特殊的处理)
     

    Sample Input

    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

    Sample Output

    2.80
     
     
     
    正解:概率DP+floyd
    解题报告:

    24分

      注意到有6个测试点m=0,则说明不能提出申请,那么只需要求出全图的两两之间的最短路,路径唯一确定。

     

    52分

      注意到另外有7个测试点m=1,只能提出一次申请。我们可以直接枚举在哪里提出申请,再与不申请的时候取一个min就可以了。

     

    76分

      注意到还有6个测试点m=2,只能提出两次申请,暴力枚举哪两个点申请即可。

     

    80分

      其实我们并不需要分那么多类情况讨论,考虑直接爆搜,在每个点是否提出申请,最后暴力计算贡献。因为这样的复杂度是C(n,m)的,所以m<=2和n<=10是完全没有问题的,直接可以用搜索拿到80分。

     

    100分

      这显然是一道概率DP裸题。考虑f[i][j][0、1]表示前i堂课,已经申请了j次,这次申不申请的最小期望值,则:

     $${f[i][j][0]=min( f[i-1][j][0]+dis(c[i-1],c[i]),f[i-1][j][1]+dis(c[i-1],c[i])*(1-p[i-1])+dis(d[i-1],c[i])*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(c[i-1],c[i])*(1-p[i-1])*(1-p[i])+dis(c[i-1],d[i])*(1-p[i-1])*p[i]+dis(d[i-1],c[i])*p[i-1]*(1-p[i]))+dis(d[i-1],d[i])*p[i-1]*p[i]) }$$

      

     (dis(i,j)表示i到j的最短距离)

     

      需要说明的是,期望是可以线性相加的,也就是说一条路径上的距离总期望值实际上就等于每两个相邻点之间的距离的期望值的总和。另外上面的转移式可以这样理解:在我们已经提出申请的情况下,对于所有情况的讨论就是在现有的申请情况下的能得到的距离期望值,事实上是根据计算了每条边的贡献。

      这样即可获得满分。时间复杂度:O(V3+nm)

     

    注意事项

          如果状态的0、1表示的是地点在c还是d的话会有不少问题,因为压根就不能体现出申请的时候的成功与失败结果,但是对于m<=2的点基本不会出错,大点的出错概率也不是特别大,就导致这样完全错误的状态设计在联赛数据下获得了88分……

     
     
     1 //It is made by ljh2000
     2 #include <iostream>
     3 #include <cstdlib>
     4 #include <cstring>
     5 #include <cstdio>
     6 #include <cmath>
     7 #include <algorithm>
     8 #include <ctime>
     9 #include <vector>
    10 #include <queue>
    11 #include <map>
    12 #include <set>
    13 #include <string>
    14 using namespace std;
    15 typedef long long LL;
    16 const int MAXN = 2011;
    17 const int MAXD = 311;
    18 const int inf = (1<<29); 
    19 int n,m,D,bian,c[MAXN],d[MAXN],dis[MAXD][MAXD];
    20 double f[MAXN][MAXN][2],k[MAXN],ans;
    21 
    22 inline int getint(){
    23     int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
    24     if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
    25 }
    26 
    27 inline void work(){
    28     n=getint(); m=getint(); D=getint(); bian=getint(); int x,y,z,lim; double minl;
    29     for(int i=1;i<=n;i++) c[i]=getint(); for(int i=1;i<=n;i++) d[i]=getint(); for(int i=1;i<=n;i++) scanf("%lf",&k[i]);
    30     for(int i=1;i<=D;i++) for(int j=1;j<=D;j++) dis[i][j]=inf;
    31     for(int i=1;i<=bian;i++) {
    32         x=getint(); y=getint(); z=getint();    if(dis[x][y]==inf) dis[x][y]=dis[y][x]=z;
    33         else dis[x][y]=min(dis[x][y],z),dis[y][x]=dis[x][y];
    34     }
    35     for(int l=1;l<=D;l++) for(int i=1;i<=D;i++) if(i!=l) for(int j=1;j<=D;j++) if(j!=l&&i!=j) dis[i][j]=min(dis[i][l]+dis[l][j],dis[i][j]); 
    36     for(int i=1;i<=n;i++) for(int j=0;j<=m;j++) f[i][j][0]=f[i][j][1]=1e30; f[1][0][0]=f[1][1][1]=0; 
    37     for(int i=1;i<=D;i++) dis[i][i]=0;
    38     for(int i=2;i<=n;i++) {
    39         lim=min(i,m);
    40         for(int j=0;j<=lim;j++) {
    41             minl=f[i-1][j][1]+dis[c[i-1]][c[i]]*(1.0-k[i-1])+dis[d[i-1]][c[i]]*k[i-1];
    42             minl=min(minl,f[i-1][j][0]+dis[c[i-1]][c[i]]);
    43             f[i][j][0]=min(f[i][j][0],minl);
    44             if(j>=1) {
    45                 minl=f[i-1][j-1][1]+dis[c[i-1]][c[i]]*(1.0-k[i])*(1.0-k[i-1])+dis[c[i-1]][d[i]]*(1.0-k[i-1])*k[i];
    46                 minl+=dis[d[i-1]][c[i]]*k[i-1]*(1-k[i])+dis[d[i-1]][d[i]]*k[i-1]*k[i];
    47                 minl=min(minl,f[i-1][j-1][0]+dis[c[i-1]][d[i]]*k[i]+dis[c[i-1]][c[i]]*(1.0-k[i]));
    48                 f[i][j][1]=min(f[i][j][1],minl);
    49             }
    50         }
    51     }
    52     ans=1e30; for(int i=0;i<=m;i++) ans=min(ans,min(f[n][i][0],f[n][i][1]));
    53     printf("%.2lf",ans);
    54 }
    55 
    56 int main()
    57 {
    58     work();
    59     return 0;
    60 }
     
  • 相关阅读:
    Springboot~多个数据源时自定义datasource的bean
    springboot~aspose操作word模板实现导出功能
    spring-security-jwt的总结与实现
    mybatis+maven自动生成代码框架
    chrome 插件 vimium 像操作vim一样的操作浏览器
    递归计算过程和迭代计算过程
    找工作--Java相关
    《Linux程序设计》--读书笔记---第十三章进程间通信:管道
    poj 1474 Video Surveillance
    动态包含与静态包含的区别
  • 原文地址:https://www.cnblogs.com/ljh2000-jump/p/6189054.html
Copyright © 2011-2022 走看看